我有一个脚本可以测量某些命令执行的时间。
它需要“真实”time
命令,即二进制文件/usr/bin/time
(因为 bash 内置命令没有该-f
标志)。
下面是一个可以调试的简化脚本:
#!/bin/bash
TIMESEC=$(echo blah | ( /usr/bin/time -f %e grep blah >/dev/null ) 2>&1 | awk -F. '{print $1}')
echo ABC--$TIMESEC--DEF
if [ "$TIMESEC" -eq 0 ] ; then
echo "we are here!"
fi
保存为“test.sh”并执行:
$ bash test.sh
ABC--0--DEF
we are here!
所以它起作用了。
现在,让我们尝试通过在 bash 命令行中添加“-x”来调试它:
$ bash -x test.sh
++ echo blah
++ awk -F. '{print $1}'
+ TIMESEC='++ /usr/bin/time -f %e grep blah
0'
+ echo ABC--++ /usr/bin/time -f %e grep blah 0--DEF
ABC--++ /usr/bin/time -f %e grep blah 0--DEF
+ '[' '++ /usr/bin/time -f %e grep blah
0' -eq 0 ']'
test.sh: line 10: [: ++ /usr/bin/time -f %e grep blah
0: integer expression expected
为什么当我们使用“-x”时这个脚本会中断,而没有它却可以正常工作?
答案1
问题是这一行:
TIMESEC=$(echo blah | ( /usr/bin/time -f %e grep blah >/dev/null ) 2>&1 | awk -F. '{print $1}')
您正在重定向标准错误以匹配标准输出。 bash 正在将其跟踪消息写入标准错误,并且(例如)在 bash 进程中使用其内置函数echo
以及其他 shell 结构。
如果你把它改成类似的东西
TIMESEC=$(echo blah | sh -c "( /usr/bin/time -f %e grep blah >/dev/null )" 2>&1 | awk -F. '{print $1}')
它将解决这个问题,并且也许是跟踪和工作之间可接受的折衷方案:
++ awk -F. '{print $1}'
++ sh -c '( /usr/bin/time -f %e grep blah >/dev/null )'
++ echo blah
+ TIMESEC=0
+ echo ABC--0--DEF
ABC--0--DEF
+ '[' 0 -eq 0 ']'
+ echo 'we are here!'
we are here!
答案2
你也可以直接删除子shell。显然,这是嵌套的贝壳最终导致彼此不安:
TIMESEC=$(
echo blah |
/usr/bin/time -f %e grep blah 2>&1 >/dev/null |
awk -F. '{print $1}'
)
如果你这样做:
...| ( subshell ) 2>pipe | ...
...您最终会启动子 shell 来处理管道的该部分托管里面的子壳。因为 shell 甚至没有重定向子 shell 中的调试输出(它也适用于您可能选择使用的任何其他类型的{
复合命令); } >redirect
在管道的其部分,您将混合流。它与重定向的顺序有关。
相反,如果您只是先重定向仅有的您尝试测量的命令的错误输出,并让主机 shell 的输出将其发送到 stderr,您就不会遇到同样的问题。
所以...
... | command 2>pipe 1>/dev/null | ...
...主机 shell 可以自由地继续在其喜欢的地方写入其 stderr,同时仅将其调用的命令的输出重定向到管道中。
bash -x time.sh
+++ echo blah
+++ /usr/bin/time -f %e grep blah
+++ awk -F. '{print $1}'
++ TIMESEC=0
++ echo ABC--0--DEF
ABC--0--DEF
++ '[' 0 -eq 0 ']'
++ echo 'we are here!'
we are here!
对于这个问题...
TIMESEC=$(
echo blah |
/usr/bin/time -f %e grep blah 2>&1 >/dev/null
)
printf %s\\n "ABC--${TIMESEC%%.*}--DEF"
if [ "${TIMESEC%%.*}" -eq 0 ] ; then
echo "we are here!"
fi