在 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,但这是一个很好的做法。它使 cmndret_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 正在safeRunCommandprintf 语句中使用。您可以使用它,因为它是全球性的。所以我要把$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.txtgrep: 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) 中获取命令的退出代码的主要内容,如果未能解决你的问题,请参考以下文章

KornShell (ksh) 环绕

安装KornShell(KSH)

如何在Ksh中把clear绑定到^L上?

korn vs bash 退出代码

我可以在 KornShell 中获取当前脚本的绝对路径吗?

.sh 和 .ksh —— 三种主要的 Shell简介(Korn shell)