是否有类似于pipefail的多个命令,比如'try'语句但在bash中.我想做这样的事情:
echo "trying stuff"
try {
command1
command2
command3
}
Run Code Online (Sandbox Code Playgroud)
并且在任何时候,如果任何命令失败,则退出并回显该命令的错误.我不想做以下事情:
command1
if [ $? -ne 0 ]; then
echo "command1 borked it"
fi
command2
if [ $? -ne 0 ]; then
echo "command2 borked it"
fi
Run Code Online (Sandbox Code Playgroud)
等等......或类似的东西:
pipefail -o
command1 "arg1" "arg2" | command2 "arg1" "arg2" | command3
Run Code Online (Sandbox Code Playgroud)
因为我相信的每个命令的参数(如果我错了,纠正我)会相互干扰.这两种方法对我来说似乎非常啰嗦和讨厌,所以我在这里呼吁采用更有效的方法.
krt*_*tek 270
您可以编写一个为您启动和测试命令的函数.假设command1
并且command2
是已设置为命令的环境变量.
function mytest {
"$@"
local status=$?
if [ $status -ne 0 ]; then
echo "error with $1" >&2
fi
return $status
}
mytest $command1
mytest $command2
Run Code Online (Sandbox Code Playgroud)
Wil*_*ell 182
你是什么意思"退出并回应错误"?如果您的意思是希望脚本在任何命令失败后立即终止,那么就这样做
set -e # DON'T do this. See commentary below.
Run Code Online (Sandbox Code Playgroud)
在脚本的开头(但请注意下面的警告).不要打扰回显错误消息:让失败的命令处理它.换句话说,如果你这样做:
#!/bin/sh
set -e # Use caution. eg, don't do this
command1
command2
command3
Run Code Online (Sandbox Code Playgroud)
和command2失败,在向stderr打印错误消息时,似乎你已经实现了你想要的.(除非我误解了你想要的东西!)
作为推论,您编写的任何命令都必须表现良好:它必须向stderr报告错误而不是stdout(问题中的示例代码将错误打印到stdout),并且当它失败时它必须以非零状态退出.
但是,我不再认为这是一个好习惯. set -e
已经使用不同版本的bash改变了它的语义,虽然它适用于一个简单的脚本,但是有很多边缘情况它基本上无法使用.(考虑一下这样的事情:set -e; foo() { false; echo should not print; } ; foo && echo ok
这里的语义有些合理,但如果你将代码重构为依赖于选项设置提前终止的函数,你很容易被咬掉.)IMO最好写:
#!/bin/sh
command1 || exit
command2 || exit
command3 || exit
Run Code Online (Sandbox Code Playgroud)
要么
#!/bin/sh
command1 && command2 && command3
Run Code Online (Sandbox Code Playgroud)
Joh*_*ica 86
我有一套脚本功能,我在我的Red Hat系统上广泛使用.他们使用系统功能/etc/init.d/functions
来打印绿色[ OK ]
和红色[FAILED]
状态指示器.
$LOG_STEPS
如果要记录哪些命令失败,可以选择将变量设置为日志文件名.
step "Installing XFS filesystem tools:"
try rpm -i xfsprogs-*.rpm
next
step "Configuring udev:"
try cp *.rules /etc/udev/rules.d
try udevtrigger
next
step "Adding rc.postsysinit hook:"
try cp rc.postsysinit /etc/rc.d/
try ln -s rc.d/rc.postsysinit /etc/rc.postsysinit
try echo $'\nexec /etc/rc.postsysinit' >> /etc/rc.sysinit
next
Run Code Online (Sandbox Code Playgroud)
Installing XFS filesystem tools: [ OK ]
Configuring udev: [FAILED]
Adding rc.postsysinit hook: [ OK ]
Run Code Online (Sandbox Code Playgroud)
#!/bin/bash
. /etc/init.d/functions
# Use step(), try(), and next() to perform a series of commands and print
# [ OK ] or [FAILED] at the end. The step as a whole fails if any individual
# command fails.
#
# Example:
# step "Remounting / and /boot as read-write:"
# try mount -o remount,rw /
# try mount -o remount,rw /boot
# next
step() {
echo -n "$@"
STEP_OK=0
[[ -w /tmp ]] && echo $STEP_OK > /tmp/step.$$
}
try() {
# Check for `-b' argument to run command in the background.
local BG=
[[ $1 == -b ]] && { BG=1; shift; }
[[ $1 == -- ]] && { shift; }
# Run the command.
if [[ -z $BG ]]; then
"$@"
else
"$@" &
fi
# Check if command failed and update $STEP_OK if so.
local EXIT_CODE=$?
if [[ $EXIT_CODE -ne 0 ]]; then
STEP_OK=$EXIT_CODE
[[ -w /tmp ]] && echo $STEP_OK > /tmp/step.$$
if [[ -n $LOG_STEPS ]]; then
local FILE=$(readlink -m "${BASH_SOURCE[1]}")
local LINE=${BASH_LINENO[0]}
echo "$FILE: line $LINE: Command \`$*' failed with exit code $EXIT_CODE." >> "$LOG_STEPS"
fi
fi
return $EXIT_CODE
}
next() {
[[ -f /tmp/step.$$ ]] && { STEP_OK=$(< /tmp/step.$$); rm -f /tmp/step.$$; }
[[ $STEP_OK -eq 0 ]] && echo_success || echo_failure
echo
return $STEP_OK
}
Run Code Online (Sandbox Code Playgroud)
Joh*_*ica 50
对于它的价值,编写代码以检查每个命令是否成功的简短方法是:
command1 || echo "command1 borked it"
command2 || echo "command2 borked it"
Run Code Online (Sandbox Code Playgroud)
它仍然很乏味,但至少它是可读的.
dim*_*414 35
另一种方法是简单地将命令连接在一起,&&
以便第一个失败防止其余的执行:
command1 &&
command2 &&
command3
Run Code Online (Sandbox Code Playgroud)
这不是您在问题中要求的语法,但它是您描述的用例的常见模式.通常,命令应该负责打印失败,因此您不必手动执行此操作(可能带有-q
标记以在您不需要时将错误静音).如果你有能力修改这些命令,我会编辑它们以便在失败时大喊大叫,而不是将它们包装在其他类似命令中.
另请注意,您无需执行以下操作:
command1
if [ $? -ne 0 ]; then
Run Code Online (Sandbox Code Playgroud)
你可以简单地说:
if ! command1; then
Run Code Online (Sandbox Code Playgroud)
Pau*_*ce. 31
而不是创建转轮功能或使用set -e
,使用trap
:
trap 'echo "error"; do_cleanup failed; exit' ERR
trap 'echo "received signal to stop"; do_cleanup interrupted; exit' SIGQUIT SIGTERM SIGINT
do_cleanup () { rm tempfile; echo "$1 $(date)" >> script_log; }
command1
command2
command3
Run Code Online (Sandbox Code Playgroud)
陷阱甚至可以访问触发它的命令的行号和命令行.变量是$BASH_LINENO
和$BASH_COMMAND
.
sle*_*cal 14
我个人更喜欢使用轻量级的方法,因为看到这里 ;
yell() { echo "$0: $*" >&2; }
die() { yell "$*"; exit 111; }
try() { "$@" || die "cannot $*"; }
asuser() { sudo su - "$1" -c "${*:2}"; }
Run Code Online (Sandbox Code Playgroud)
用法示例:
try apt-fast upgrade -y
try asuser vagrant "echo 'uname -a' >> ~/.profile"
Run Code Online (Sandbox Code Playgroud)
run() {
$*
if [ $? -ne 0 ]
then
echo "$* failed with exit code $?"
return 1
else
return 0
fi
}
run command1 && run command2 && run command3
Run Code Online (Sandbox Code Playgroud)
我在bash中开发了一个几乎完美无缺的try&catch实现,允许你编写如下代码:
try
echo 'Hello'
false
echo 'This will not be displayed'
catch
echo "Error in $__EXCEPTION_SOURCE__ at line: $__EXCEPTION_LINE__!"
Run Code Online (Sandbox Code Playgroud)
你甚至可以将try-catch块嵌入其中!
try {
echo 'Hello'
try {
echo 'Nested Hello'
false
echo 'This will not execute'
} catch {
echo "Nested Caught (@ $__EXCEPTION_LINE__)"
}
false
echo 'This will not execute too'
} catch {
echo "Error in $__EXCEPTION_SOURCE__ at line: $__EXCEPTION_LINE__!"
}
Run Code Online (Sandbox Code Playgroud)
代码是我的bash样板/框架的一部分.它进一步扩展了try&catch的概念,例如带有回溯和异常的错误处理(以及一些其他不错的功能).
这是负责try&catch的代码:
set -o pipefail
shopt -s expand_aliases
declare -ig __oo__insideTryCatch=0
# if try-catch is nested, then set +e before so the parent handler doesn't catch us
alias try="[[ \$__oo__insideTryCatch -gt 0 ]] && set +e;
__oo__insideTryCatch+=1; ( set -e;
trap \"Exception.Capture \${LINENO}; \" ERR;"
alias catch=" ); Exception.Extract \$? || "
Exception.Capture() {
local script="${BASH_SOURCE[1]#./}"
if [[ ! -f /tmp/stored_exception_source ]]; then
echo "$script" > /tmp/stored_exception_source
fi
if [[ ! -f /tmp/stored_exception_line ]]; then
echo "$1" > /tmp/stored_exception_line
fi
return 0
}
Exception.Extract() {
if [[ $__oo__insideTryCatch -gt 1 ]]
then
set -e
fi
__oo__insideTryCatch+=-1
__EXCEPTION_CATCH__=( $(Exception.GetLastException) )
local retVal=$1
if [[ $retVal -gt 0 ]]
then
# BACKWARDS COMPATIBILE WAY:
# export __EXCEPTION_SOURCE__="${__EXCEPTION_CATCH__[(${#__EXCEPTION_CATCH__[@]}-1)]}"
# export __EXCEPTION_LINE__="${__EXCEPTION_CATCH__[(${#__EXCEPTION_CATCH__[@]}-2)]}"
export __EXCEPTION_SOURCE__="${__EXCEPTION_CATCH__[-1]}"
export __EXCEPTION_LINE__="${__EXCEPTION_CATCH__[-2]}"
export __EXCEPTION__="${__EXCEPTION_CATCH__[@]:0:(${#__EXCEPTION_CATCH__[@]} - 2)}"
return 1 # so that we may continue with a "catch"
fi
}
Exception.GetLastException() {
if [[ -f /tmp/stored_exception ]] && [[ -f /tmp/stored_exception_line ]] && [[ -f /tmp/stored_exception_source ]]
then
cat /tmp/stored_exception
cat /tmp/stored_exception_line
cat /tmp/stored_exception_source
else
echo -e " \n${BASH_LINENO[1]}\n${BASH_SOURCE[2]#./}"
fi
rm -f /tmp/stored_exception /tmp/stored_exception_line /tmp/stored_exception_source
return 0
}
Run Code Online (Sandbox Code Playgroud)
随意使用,分叉和贡献 - 它在GitHub上.
归档时间: |
|
查看次数: |
372792 次 |
最近记录: |