Tôi có một kịch bản shell thực thi một số lệnh. Làm cách nào để thực hiện thoát lệnh shell shell nếu bất kỳ lệnh nào thoát với mã thoát khác không?
$?
sau mỗi lệnh. Phương pháp dễ dàng: đặt set -e
hoặc #!/bin/bash -e
ở đầu tập lệnh Bash của bạn.
Tôi có một kịch bản shell thực thi một số lệnh. Làm cách nào để thực hiện thoát lệnh shell shell nếu bất kỳ lệnh nào thoát với mã thoát khác không?
$?
sau mỗi lệnh. Phương pháp dễ dàng: đặt set -e
hoặc #!/bin/bash -e
ở đầu tập lệnh Bash của bạn.
Câu trả lời:
Sau mỗi lệnh, mã thoát có thể được tìm thấy trong $?
biến để bạn có thể có một cái gì đó như:
ls -al file.ext
rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi
Bạn cần cẩn thận với các lệnh đường ống vì $?
chỉ cung cấp cho bạn mã trả về của phần tử cuối cùng trong đường ống, vì vậy, trong mã:
ls -al file.ext | sed 's/^/xx: /"
sẽ không trả về mã lỗi nếu tệp không tồn tại (vì sed
một phần của đường ống thực sự hoạt động, trả về 0).
Các bash
vỏ thực sự cung cấp một mảng mà có thể hỗ trợ trong trường hợp đó, đó là PIPESTATUS
. Mảng này có một phần tử cho mỗi thành phần đường ống, mà bạn có thể truy cập riêng lẻ như ${PIPESTATUS[0]}
:
pax> false | true ; echo ${PIPESTATUS[0]}
1
Lưu ý rằng điều này sẽ giúp bạn nhận được kết quả của false
lệnh chứ không phải toàn bộ đường ống. Bạn cũng có thể lấy toàn bộ danh sách để xử lý khi bạn thấy phù hợp:
pax> false | true | false; echo ${PIPESTATUS[*]}
1 0 1
Nếu bạn muốn lấy mã lỗi lớn nhất từ một đường ống, bạn có thể sử dụng một cái gì đó như:
true | true | false | true | false
rcs=${PIPESTATUS[*]}; rc=0; for i in ${rcs}; do rc=$(($i > $rc ? $i : $rc)); done
echo $rc
Điều này PIPESTATUS
lần lượt đi qua từng phần tử, lưu trữ nó rc
nếu nó lớn hơn rc
giá trị trước đó .
ls -al file.ext || exit $?
([[]] không khả dụng)
[[ ]]
khá dễ mang theo bash
, đó là câu hỏi được gắn thẻ :-) Thật kỳ lạ, ls
nó không hoạt động command.com
nên nó cũng không di động, tôi biết, nhưng đó là loại tranh luận tương tự với bạn hiện tại.
PIPESTATUS
(nghĩa là ${PIPESTATUS[0]}
cho lệnh đầu tiên, ${PIPESTATUS[1]}
cho lệnh thứ hai hoặc ${PIPESTATUS[*]}
cho danh sách tất cả các stati thoát.
$?
trực tiếp. Bạn thường muốn một cái gì đó giống như if ls -al file.ext; then : nothing; else exit $?; fi
trong đó tất nhiên như @MarcH nói là tương đương với ls -al file.ext || exit $?
nhưng nếu then
hay else
điều khoản có phần phức tạp hơn, nó là dễ bảo trì hơn.
[[ $rc != 0 ]]
sẽ cung cấp cho bạn một 0: not found
hoặc một 1: not found
lỗi. Điều này nên được thay đổi thành [ $rc -ne 0 ]
. Cũng rc=$?
có thể được gỡ bỏ và chỉ cần sử dụng [ $? -ne 0 ]
.
Nếu bạn muốn làm việc với $?, Bạn sẽ cần kiểm tra nó sau mỗi lệnh, kể từ $? được cập nhật sau mỗi lần thoát lệnh. Điều này có nghĩa là nếu bạn thực hiện một đường ống, bạn sẽ chỉ nhận được mã thoát của quy trình cuối cùng trong đường ống.
Một cách tiếp cận khác là làm điều này:
set -e
set -o pipefail
Nếu bạn đặt cái này ở đầu tập lệnh shell, có vẻ như bash sẽ lo việc này cho bạn. Như một poster trước đã lưu ý, "set -e" sẽ khiến bash thoát với lỗi trên bất kỳ lệnh đơn giản nào. "set -o pipefail" cũng sẽ khiến bash thoát với lỗi trên bất kỳ lệnh nào trong đường ống.
Xem ở đây hoặc ở đây để thảo luận thêm một chút về vấn đề này. Đây là phần hướng dẫn sử dụng bash trên tập hợp dựng sẵn.
PIPESTATUS
và kiểm tra mã thoát ở mọi nơi.
#!/bin/bash -e
là cách duy nhất để bắt đầu một kịch bản shell. Bạn luôn có thể sử dụng những thứ như foo || handle_error $?
nếu bạn thực sự cần kiểm tra trạng thái thoát.
" set -e
" Có lẽ là cách dễ nhất để làm điều này. Chỉ cần đặt nó trước bất kỳ lệnh nào trong chương trình của bạn.
set -e
tập lệnh của bạn sẽ hủy bỏ nếu bất kỳ lệnh nào trong tập lệnh của bạn thoát với trạng thái lỗi và bạn đã không xử lý lỗi này.
set -e
là 100% tương đương với set -o errexit
cái mà không giống như trước đây có thể được tìm kiếm. Tìm kiếm opengroup + errexit cho các tài liệu chính thức.
Nếu bạn chỉ gọi exit trong bash không có tham số, nó sẽ trả về mã thoát của lệnh cuối cùng. Kết hợp với OR, bash chỉ nên gọi thoát, nếu lệnh trước đó không thành công. Nhưng tôi đã không kiểm tra điều này.
lệnh1 | | lối ra; lệnh2 | | lối ra;
Bash cũng sẽ lưu mã thoát của lệnh cuối cùng trong biến $?.
http://cfaj.freeshell.org/shell/cus-faq-2.html#11
Làm thế nào để lấy mã lối ra cmd1
vàocmd1|cmd2
Đầu tiên, lưu ý rằng cmd1
mã thoát có thể khác không và vẫn không có nghĩa là lỗi. Điều này xảy ra ví dụ trong
cmd | head -1
bạn có thể quan sát trạng thái thoát 141 (hoặc 269 với ksh93) cmd1
, nhưng vì nó cmd
bị gián đoạn bởi tín hiệu SIGPIPE khi
head -1
bị chấm dứt sau khi đọc một dòng.
Để biết trạng thái thoát của các thành phần của đường ống
cmd1 | cmd2 | cmd3
a. với zsh:
Các mã thoát được cung cấp trong mảng đặc biệt pipestatus.
cmd1
mã thoát là trong $pipestatus[1]
, cmd3
mã thoát trong
$pipestatus[3]
, vì vậy $?
luôn luôn giống như
$pipestatus[-1]
.
b. với bash:
Các mã thoát được cung cấp trong PIPESTATUS
mảng đặc biệt.
cmd1
mã thoát là trong ${PIPESTATUS[0]}
, cmd3
mã thoát trong
${PIPESTATUS[2]}
, vì vậy $?
luôn luôn giống như
${PIPESTATUS: -1}
.
...
Để biết thêm chi tiết xem liên kết sau .
cho bash:
# this will trap any errors or commands with non-zero exit status
# by calling function catch_errors()
trap catch_errors ERR;
#
# ... the rest of the script goes here
#
function catch_errors() {
# do whatever on errors
#
#
echo "script aborted, because of errors";
exit 0;
}
Trong bash, điều này thật dễ dàng, chỉ cần gắn chúng lại với &&:
command1 && command2 && command3
Bạn cũng có thể sử dụng lồng nhau nếu cấu trúc:
if command1
then
if command2
then
do_something
else
exit
fi
else
exit
fi
if (! command)
nếu bạn mong đợi một mã lỗi khác không từ lệnh.
#
#------------------------------------------------------------------------------
# run a command on failure exit with message
# doPrintHelp: doRunCmdOrExit "$cmd"
# call by:
# set -e ; doRunCmdOrExit "$cmd" ; set +e
#------------------------------------------------------------------------------
doRunCmdOrExit(){
cmd="$@" ;
doLog "DEBUG running cmd or exit: \"$cmd\""
msg=$($cmd 2>&1)
export exit_code=$?
# if occured during the execution exit with error
error_msg="Failed to run the command:
\"$cmd\" with the output:
\"$msg\" !!!"
if [ $exit_code -ne 0 ] ; then
doLog "ERROR $msg"
doLog "FATAL $msg"
doExit "$exit_code" "$error_msg"
else
#if no errors occured just log the message
doLog "DEBUG : cmdoutput : \"$msg\""
doLog "INFO $msg"
fi
}
#eof func doRunCmdOrExit
$*
; sử dụng "$@"
thay thế để bảo tồn không gian và ký tự đại diện.