如何有条件地通过“时间”传递子外壳?

如何有条件地通过“时间”传递子外壳?

我有一个 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)'

这里有什么问题?

答案1

有能力去时间一个子shell,你需要time 关键词,不是命令。

关键字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 关键词不接受选项(除了-pin ),但可以使用变量 inbash来设置格式。 (该部分也是特定的,其他外壳通常不需要)。TIMEFORMATbashshoptbash

答案2

虽然这alias是一种方法,但这也完成了eval- 只是你不太想要eval命令执行,而是想要eval命令宣言

我喜欢aliases - 我一直使用它们,但我更喜欢函数 - 特别是它们处理参数的能力,并且它们不一定需要像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 in 之间的分隔符"$*"是 中的第一个字节$IFS,因此在不确定其值的情况下继续操作可能会很危险。因此,该函数保存$IFS,将其设置为\n足够长的 ewline ,以将其set ... "$*"放入,然后重置其值(如果以前有)。"$3"unset

这是一个小演示:

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

你看,我在其中的值中放置了两个命令$TIME- 任何数字都可以 - 甚至没有 - 但请确保它被转义并正确引用 - 的参数也是如此_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

相关内容