在 Bash 和 KornShell (ksh) 中获取命令的退出代码
Posted
技术标签:
【中文标题】在 Bash 和 KornShell (ksh) 中获取命令的退出代码【英文标题】:Get the exit code for a command in Bash and KornShell (ksh) 【发布时间】:2012-01-02 22:53:32 【问题描述】:我想写这样的代码:
command="some command"
safeRunCommand $command
safeRunCommand()
cmnd=$1
$($cmnd)
if [ $? != 0 ]; then
printf "Error when executing command: '$command'"
exit $ERROR_CODE
fi
但是这段代码不能按我想要的方式工作。我在哪里做错了?
【问题讨论】:
【参考方案1】:下面是固定代码:
#!/bin/ksh
safeRunCommand()
typeset cmnd="$*"
typeset ret_code
echo cmnd=$cmnd
eval $cmnd
ret_code=$?
if [ $ret_code != 0 ]; then
printf "Error: [%d] when executing command: '$cmnd'" $ret_code
exit $ret_code
fi
command="ls -l | grep p"
safeRunCommand "$command"
现在,如果您查看这段代码,我更改的几处是:
没有必要使用typeset
,但这是一个很好的做法。它使 cmnd
和 ret_code
本地化到 safeRunCommand
没有必要使用ret_code
,但最好将返回码存储在某个变量中(并尽快存储),以便您以后可以像我在printf "Error: [%d] when executing command: '$command'" $ret_code
中那样使用它
传递命令时使用引号将命令括起来,例如safeRunCommand "$command"
。如果你不这样做,那么cmnd
将只获得值ls
而不是ls -l
。如果您的命令包含管道,则更为重要。
如果您想保留空格,可以使用typeset cmnd="$*"
而不是typeset cmnd="$1"
。您可以根据命令参数的复杂程度同时尝试两者。
'eval' 用于评估,以便包含管道的命令可以正常工作
注意:请记住一些命令将 1 作为返回码,即使没有像 grep
这样的错误。如果grep
发现了什么,它将返回 0,否则返回 1。
我用KornShell 和Bash 进行了测试。它工作得很好。如果您在运行此程序时遇到问题,请告诉我。
【讨论】:
您可以看到$command
正在safeRunCommand
的printf
语句中使用。您可以使用它,因为它是全球性的。所以我要把$command
改成$cmnd
(局部变量..
谢谢@havexz。我怎样才能像这样运行命令:command="ls -l | grep *.log"
不幸的是,这个命令不起作用,而且我的所有命令都非常复杂,有许多管道 |、grep、awk、sed、...
更新了代码,只保留了更健壮的一个soln。
还有一个问题 :) 如何将函数 safeRunCommand
的输出添加到变量中,以防处理中断失败功能。
也许我找到了答案。我可以写out=$(eval $cmnd)
,并在调用safeRunCommand
函数后使用out
变量吗?【参考方案2】:
试试
safeRunCommand()
"$@"
if [ $? != 0 ]; then
printf "Error when executing command: '$1'"
exit $ERROR_CODE
fi
【讨论】:
【参考方案3】:应该是$cmd
而不是$($cmd)
。它在我的盒子上很好用。
您的脚本仅适用于单字命令,例如 ls。它不适用于“ls cpp”。为此,请将cmd="$1"; $cmd
替换为"$@"
。并且,不要以command="some cmd"; safeRun command
运行您的脚本。以safeRun some cmd
运行它。
此外,当您必须调试 Bash 脚本时,请使用“-x”标志执行。 [bash -x s.sh]。
【讨论】:
快速详细说明为什么这是正确的:$($cmd)
将在 new shell 中执行 $cmd
,而 $cmd
不会。
cmd="$@"
不起作用;相反,要么直接运行"$@"
(如@sehe's answer),要么使用数组:cmd=("$@"); "$cmd[@]"
@GordonDavisson:我先在我的系统上试一下,然后再在这里发布任何内容。它工作正常。这是 bash -x try.sh 上的输出。 + command='ls java' + safeRunCommand ls java + cmnd='ls java' + ls java
。和 cmd="hello world" 一样,不是吗?
@logic_max:在参数中有空格的命令上试试,比如safeRunCommand grep "this that" file.txt
:+ cmd='grep this that file.txt'
+ grep this that file.txt
grep: that: No such file or directory
@logic_max:是的。我不确定为什么人们坚持在执行命令之前将命令存储在变量中;它只是创造了这样的限制。【参考方案4】:
您的脚本有几处问题。
函数(子例程)应在尝试调用它们之前声明。您可能希望从子例程中返回()而不是退出(),以允许调用块测试特定命令的成功或失败。除此之外,您不会捕获“ERROR_CODE”,因此它始终为零(未定义)。
用花括号将变量引用括起来也是一种很好的做法。您的代码可能如下所示:
#!/bin/sh
command="/bin/date -u" #...Example Only
safeRunCommand()
cmnd="$@" #...insure whitespace passed and preserved
$cmnd
ERROR_CODE=$? #...so we have it for the command we want
if [ $ERROR_CODE != 0 ]; then
printf "Error when executing command: '$command'\n"
exit $ERROR_CODE #...consider 'return()' here
fi
safeRunCommand $command
command="cp"
safeRunCommand $command
【讨论】:
cmnd="$@"
不起作用;相反,要么直接运行"$@"
(如@sehe's answer),要么使用数组:cmnd=("$@"); "$cmnd[@]"
【参考方案5】:
通常的想法是运行命令,然后使用$?
获取退出代码。但是,有时您有多种情况需要获取退出代码。例如,您可能需要隐藏其输出,但仍返回退出代码,或者同时打印退出代码和输出。
ec() [[ "$1" == "-h" ]] && shift && eval $* > /dev/null 2>&1; ec=$?; echo $ec; || eval $*; ec=$?;
这将使您可以选择禁止输出您想要退出代码的命令。当命令的输出被抑制时,退出代码将直接由函数返回。
我个人喜欢把这个函数放在我的.bashrc file中。
下面我将展示一些你可以使用它的方法:
# In this example, the output for the command will be
# normally displayed, and the exit code will be stored
# in the variable $ec.
$ ec echo test
test
$ echo $ec
0
# In this example, the exit code is output
# and the output of the command passed
# to the `ec` function is suppressed.
$ echo "Exit Code: $(ec -h echo test)"
Exit Code: 0
# In this example, the output of the command
# passed to the `ec` function is suppressed
# and the exit code is stored in `$ec`
$ ec -h echo test
$ echo $ec
0
使用此函数解决您的代码
#!/bin/bash
if [[ "$(ec -h 'ls -l | grep p')" != "0" ]]; then
echo "Error when executing command: 'grep p' [$ec]"
exit $ec;
fi
您还应该注意,您将看到的退出代码将针对正在运行的
grep
命令,因为它是最后一个正在执行的命令。不是ls
。
【讨论】:
以上是关于在 Bash 和 KornShell (ksh) 中获取命令的退出代码的主要内容,如果未能解决你的问题,请参考以下文章