我如何有条件地将子壳传递给“时间”?


9

我有一个Vagrant框的设置脚本,该脚本过去曾用来测量单个步骤time。现在,我想有条件地启用或禁用时间测量。

例如,以前的一行如下所示:

time (apt-get update > /tmp/last.log 2>&1)

现在我以为我可以做这样的事情:

MEASURE_TIME=true
[[ $MEASURE_TIME = true ]] && TIME="time --format=%e" || TIME=""

$TIME (apt-get update > /tmp/last.log 2>&1)

但这不起作用:

syntax error near unexpected token `apt-get'
`$TIME (apt-get update > /tmp/last.log 2>&1)'

这是什么问题


3
您只需要达到目标速度即可使通量电容器工作;)
goldilocks 2014年

2
@goldilocks你的意思是磁铁?;)
CVn 2014年

Answers:


13

为了能够计时一个子shell,您需要time 关键字,而不是command。

time关键字,语言的一部分,从字面上输入时才承认为和作为命令的第一个字(和在的情况下ksh93,那么下一个令牌不具有启动-)。即使输入"time"也不起作用(更不用说$TIME被当作​​对time命令的调用了)。

您可以在此处使用别名,这些别名在执行另一轮解析之前已被扩展(因此将使Shell识别该time关键字):

shopt -s expand_aliases
alias time_or_not=
TIMEFORMAT=%E

MEASURE_TIME=true
[[ $MEASURE_TIME = true ]] && alias time_or_not=time

time_or_not (apt-get update > /tmp/last.log 2>&1)

time 关键字不采取选项(除了-pbash),但格式可以与设定TIMEFORMAT在变量bash。(该shopt部分也是bash特定于此的,其他shell通常不需要它)。


您说“要对子Shell计时,您需要time关键字”。我想知道这种行为记录在哪里吗?
cuonglm

@cuonglm,见info -f bash --index-search=time
斯特凡Chazelas

3

尽管an alias是执行此操作的一种方法,但可以做到eval这一点-只是您并不想eval执行eval命令声明,而是想要执行命令。

我喜欢aliases-我一直都在使用'em,但是我更喜欢函数-特别是它们处理参数的能力,并且不必像aliases 所要求的那样在命令位置扩展它们。

所以我想也许您也想尝试一下:

_time() if   set -- "${IFS+IFS=\$2;}" "$IFS" "$@" && IFS='
';      then set -- "$1" "$2" "$*"; unset IFS
             eval "$1 $TIME ${3#"$1"?"$2"?}"
        fi

$IFS位主要是关于$*同样重要的是,( subshell bit )也是shell扩展的结果,因此为了将参数扩展为我使用的可分析字符串"$*" eval "$@"顺便说一句,除非您确定所有args都可以在空格上连接,否则请不要)。args in之间的分隔定界符"$*"是中的第一个字节$IFS,因此如果不确定其值可能会很危险。所以函数保存$IFS,将其设置为一个\newline足够长set ... "$*""$3"unset原来是这样的话,如果它以前有一个复位其值。

这是一个小演示:

TIME='set -x; time'
_time \( 'echo "$(echo any number of subshells)"' \
         'command -V time'                        \
         'hash time'                              \
      \) 'set +x'

您会看到我在其中的值中放置了两个命令$TIME-任何数字都可以-甚至没有数字-但请确保将其转义并正确引用-并且to的参数也是如此_time()。它们在执行时都将被合并为单个命令字符串-但是每个arg都有其自己的\newline,因此它们可以相对容易地散布。否则,您可以将它们全部\n合并在一起,如果需要的话,还可以将它们分开,用粗线,分号或“随您便”。只要确保单个参数代表一个命令,您在调用该命令时便会在脚本中放自己的一行。\(,例如可以,只要最终紧随其后即可\)。基本上是正常的东西。

eval获取上述代码片段时,它看起来像:

+ eval 'IFS=$2;set -x; time (
echo "$(echo any number of subshells)"
command -V time
hash time
)
set +x'

而且,其结果看起来像...

输出值

+++ echo any number of subshells
++ echo 'any number of subshells'
any number of subshells
++ command -V time
time is a shell keyword
++ hash time
bash: hash: time: not found

real    0m0.003s
user    0m0.000s
sys     0m0.000s
++ set +x

hash错误表明我没有/usr/bin/time安装(因为我没有安装command让我们知道正在运行哪个时间。尾随set +x time (可能)之后执行的另一个命令-在eval输入任何内容时,请务必小心输入命令。


有没有理由_time() { eval "$TIME $@"; }吗?使用函数具有引入不同范围为变量的缺点(不适用于子shell问题)
斯特凡Chazelas

@StéphaneChazelas-由于"$@"扩展中的引号。我喜欢一个arg,eval否则会很吓人。嗯....您如何看待不同的范围?我以为那只是function功能……
mikeserv

eval在执行之前要以非常复杂的方式加入它的args。我的意思是_time 'local var=1; blah'会作出这样的var局部的_time,或者说_time 'echo "$#"'将打印$#该的_time功能,而不是调用的那个。
斯特凡Chazelas

@StéphaneChazelas-我在这里为您完成了两个选项卡。正确- eval将其args放在空格上-正如您所说的,这正是我在这里所做的-尽管它不是初始意图。我要解决这个问题。eval荷兰国际集团"$*",而不是"$@"一个习惯我很多后捞起运行在的command not found时候ARGS在错误的地方被加入。无论如何,尽管我认为args最终会在函数中执行,但在调用时扩展它们很简单。"$#"无论如何,这就是我要做的。
mikeserv

+1虽然...漂亮的扭曲片两轮牛车的
斯特凡Chazelas
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.