命令的日志退出代码,类似于time命令


10

使用

time sleep 1

产量:

$ time sleep 1

real    0m1.005s
user    0m0.001s
sys     0m0.001s

有没有我可以用来打印退出代码的sleep命令或我想运行的任何命令?

喜欢的东西:

$ log-exit-code sleep 1

也许这足够了?

sleep 1 && echo "$?"

3
不,sleep 1 && echo $?将打印卧铺细胞的代码只当它是零...
佐藤桂

哦,你是对的
Alexander Mills

1
sleep 1 && echo "$?" || echo "$?"
jesse_b

2
我添加了一个答案,因为到目前为止所有其他答案都舍弃了原始返回值,即imo不好(在这些答案之后,$?始终为0,因为他们做的最后一件事是显示,始终有效。他们都忘记保留了)原始的返回值,并在显示后返回它。因此,他们只是将其显示给观察者,但在脚本的其余部分“隐藏”了它,现在无法使用“ $?”来查看该重要命令是否返回0或其他...)
Olivier Dulac

1
同样,下面的所有解决方案都可以轻松实现此功能。例如,库萨兰南达(Kusalananda)就是:EXIT_CODE=$(tellexit command)
jesse_b

Answers:


4

到目前为止,在我的测试中,它一直有效:

command && echo "$?" || echo "$?"

只是告诉它在成功或失败时回显退出代码。

正如佐藤在下面指出的,这基本上与以下内容相同:

command; echo "$?"

可能使and和/或command变得有价值的一件事是:

command && echo "Success! Exit Code: $?" || echo "Failure! Exit Code: $?"

如果您需要脚本像Olivier所关心的那样对退出代码执行操作,那不是问题。您的脚本可能类似于:

command
case "$?" in; 
    0) echo "Command exited with: 0"
       <command if good>
       ;;
    1) echo "Command exited with: 1"
        <command if bad>
        ;;
    255) echo "Command exited with: 255"  # for ssh maybe?
         <command if 255>
         ;;
    *) echo "Command exited with: >2"
        <command for other exit code>
        ;;
esac

13
嗯,那比刚刚command; echo $?好吗?
佐藤桂

4
我想不是。 command; echo $?是一个更好的答案。
jesse_b

呃,这似乎是最好的选择,所以我选择它作为答案,如果有人不同意,lmk
Alexander Mills

@AlexanderMills只有您知道哪个是您问题的最合适答案:-)
库萨兰达

1
我不喜欢这样:这将更改退出代码,command ; echo $?因为它echo $?总是成功,因此之后,新值$?现在为0。有一种方法可以保留原始返回码,即使在显示原始返回码之后也可以使用(例如:在脚本中,在决定如何继续之前检查返回码很重要)。请参阅我的回答,以了解几种解决方案之一(此处大多数答案都可以以相同的方式更改)
Olivier Dulac

11

cmd && echo "$?"将不起作用,因为它只能打印零(echo仅在成功完成前面的命令时执行)。

这是为您提供的简短Shell函数:

tellexit () {
    "$@"

    local err="$?"
    printf 'exit code\t%d\n' "$err" >/dev/tty
    return "$err"
}

这将以与time命令相似的方式打印给定命令的退出代码。

$ tellexit echo "hello world"
hello world
exit code       0

$ tellexit false
exit code       1

通过在函数中重定向printf/dev/tty,我们仍然可以使用tellexit重定向,而不会在标准输出或错误流中造成垃圾:

$ tellexit bash -c 'echo hello; echo world >&2' >out 2>err
exit code       0
$ cat out
hello
$ cat err
world

通过将退出代码保存在变量中,我们可以将其返回给调用者:

$ tellexit false || echo 'failed'
exit code       1
failed

如果退出代码大于128(表示由于信号而终止),则该函数的更高级版本也会打印出杀死该命令的信号:

tellexit () {
    "$@"

    local err="$?"

    if [ "$err" -gt 128 ]; then
        printf 'exit code\t%d (%s)\n' "$err" "$(kill -l "$err")" >/dev/tty
    else
        printf 'exit code\t%d\n' "$err" >/dev/tty
    fi

    return "$err"
}

测试:

$ tellexit sh -c 'kill $$'
exit code       143 (TERM)

$ tellexit sh -c 'kill -9 $$'
Killed
exit code       137 (KILL)

(该local事需要ash/ pdksh/ bash/ zsh,或者你可以把它改成typeset哪几个其他炮弹也明白。)


很酷,您将如何使用tellexit,在行动过程中展示它
Alexander Mills

1
@AlexanderMills请参阅更新。
库萨兰达

2
它应该保留原始的返回值并最终返回它。
Olivier Dulac

@OlivierDulac真的很好观察!我会修改。
库萨兰达

7

使用Shell包装函数。可能使用不同的名称。

$ exito() { "$@"; echo $?; }
$ exito true
0
$ exito false
1
$ exito echo "test test"      
test test
0
$ 

(这当然会破坏标准输出,因此请使用tty@Kusalananda所示的或不要在交互式上下文之外使用它。)

进入不可移植的领域,一些shell可以报告管道中所有命令的状态,而不仅仅是最后一个命令,例如,如果您希望从整个管道报告故障,则可以报告ZSH:

% TRAPZERR() { print >/dev/tty $pipestatus }
% perl -e 'exit 42' | perl -e 'exit 99'
42 99
% false | perl -e 'exit 42' | perl -e 'exit 99'
1 42 99
% perl -e 'exit 42' | perl -e 'exit 99' | true
% 

TRAPZERR 否则,在没有错误的情况下不会触发(基于“没有新闻就是好消息”的原则)。


1
它应该保留原始的返回值并最终返回它。显示(printf,echo)简单的事情(不是以“-”开头,等等)总是可行的,然后显示“ $?” 显示屏的显示效果现在为“ 0”
Olivier Dulac

7

GNU time为此提供了一个选项:

time -f %x sleep 1
0

传递退出代码1,除非被信号2杀死:

$ /usr/bin/time -f %x sleep 1; echo $?
0
0

$ /usr/bin/time -f %x sleep 2x; echo $?
sleep: invalid time interval ‘2x’
Try 'sleep --help' for more information.
1
1

$ /usr/bin/time -f %x sleep 60s; echo $? # Press ^C before a minute elapses
0
2

如果您想知道/处理信号杀死情况,请传递-v并grep stderr作为字符串Command terminated by signal


1感谢Olivier Dulac注意到退出代码通过了。
2另外,还要感谢StéphaneChazelas指出终止信号退出代码没有通过。


1
有趣的。仅GNU但有趣的是,因为那将(可能)保留其余代码的原始返回代码(例如,它不会像其他答案那样用“ 0”代替)
Olivier Dulac

2
0如果命令被杀死,它会返回。
斯特凡Chazelas

2
@主教,我同意在那种情况下0不太理想。但是请注意,对于被信号杀死的命令,报告内容并没有达成共识。一些壳使用返回的128+信号号。您还会看到类似256+signum384+signumsignum/256signum/256+0.5生成内核的信息(实际上是返回的状态waitpid()除以256)。有些会给出文本表示形式(例如sigintsigquit+core)。我同意,仍然可能值得在gnu.coreutils.bugs新闻组上进行讨论。
斯特凡Chazelas

3
(实际上time不是coreutils它的一部分,它本身是一个带有自己的错误邮件列表的程序包(bug-time@gnu.org))
StéphaneChazelas 17年

1
@bishop,system()传统的awks之类的东西,如仍由Brian Kernighan(由kin awk)或nawkSolaris的Solaris所维护的,来自于此awk的FreeBSD。在不限制核心转储大小时awk 'BEGIN{print system("kill -s ABRT $$")}'输出0.523438。的行为gawk最近发生了变化,与不同的行为,--traditional--posix(另见的返回值close(cmd)进行更多的变化)
斯特凡Chazelas

2

我不喜欢其他所有答案(尽管我很喜欢它们的聪明之处):它们显示退出代码,但它们也会更改它。(显示部分为真,因此返回代码为0)

这是修改后的版本:

do_and_tell () {
   "$@"
   returncode="$?"
   printf "Execution of : \n%s\n yelded a return code of: \n%s\n" "$*" "$returncode"
   return $returncode  # the interresting addition... keeps the commands return value.
}

## usage:

prompt$ do_and_tell true
Execution of : 
true
 yelded a return code of: 
0

prompt$ echo $?
0

prompt$ do_and_tell false
Execution of : 
false
 yelded a return code of: 
1

prompt$ echo $?
1

实际上,@bishop答案是唯一保持此值的唯一方法,但依赖于GNU版本的time值并不总是可用的。
Olivier Dulac

2

为了更强大一点,请将退出状态发送到单独的FD,以便可以独立于stdout / stderr对其进行处理:

exit_status() {
    "$@"
    rv=$?
    echo >&3 "$rv"
    return "$rv"
}

# exit status and stdout to stdout:
> exit_status echo "hello" 3>&1
hello
0

# exit_status to stdout, stdout to /dev/null
> exit_status echo "hello" 3>&1 1>/dev/null
0
> exit_status ehco "hello" 3>&1 1>/dev/null
ehco: command not found
127

# exit_status to stdout, stdout and stderr to /dev/null
> exit_status ehco "hello" 3>&1 &>/dev/null
127

请注意,您需要对FD3进行操作,否则它将显示Bad file descriptor

这可以进一步配置为通过让另一个程序侦听多个FD来将这些输出提供给另一个程序的输入。您可以将其用作90年代初期的Kibana :)


它应该保留原始的返回值并最终返回它。显示(printf,echo)简单的事情(不是以“-”开头,等等)总是可行的,然后显示“ $?” 显示屏的显示效果现在为“ 0”
Olivier Dulac

1

尽管所有其他(非常有趣的)答案都解决了OP提出的确切问题,但实际上,事情通常更简单。您只需将退出状态保存到变量中(紧随该命令之后),然后以所需的任何方式报告或记录它。例如,打印到/ dev / stderr或将其作为文本消息写/附加到日志文件中。并且,还保留了它,以用于后续代码中的决策/流控制。

如果在函数中:

function some-function () {
    local rc ## to avoid side effects
    ...
    some-command ## run any command
    rc=$?  ## save the return status in a variable
    echo "some-command returned $rc" ## optionally tell the user about it
    ...
    return $rc  ## at the end, if appropriate
}

如果直接在脚本中:

some-command ## run any command
rc=$?  ## save the return status in a variable
echo "some-command returned $rc" ## optionally tell the user about it
...
exit $rc    ## at the end to tell any calling script about it, if appropriate

(躲避并掩盖,因为这不能回答确切的问题。)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.