我正在尝试加速调用子shell并执行各种操作的脚本集合。我很想知道是否有任何工具可用于执行shell脚本及其嵌套shell的时间,并报告脚本的哪些部分是最昂贵的。
例如,如果我有一个类似以下的脚本。
#!/bin/bash
echo "hello"
echo $(date)
echo "goodbye"
我想知道三条线中的每一条花了多长时间。 time
只会给我一些脚本的总时间。 bash -x
很有意思,但不包括时间戳或其他时间信息。
答案 0 :(得分:44)
您可以设置PS4
以显示时间和行号。这样做不需要安装任何实用程序和工作,而无需将stderr重定向到stdout。
对于这个脚本:
#!/bin/bash -x
# Note the -x flag above, it is required for this to work
PS4='+ $(date "+%s.%N ($LINENO) ")'
for i in {0..2}
do
echo $i
done
sleep 1
echo done
输出如下:
+ PS4='+ $(date "+%s.%N ($LINENO) ")'
+ 1291311776.108610290 (3) for i in '{0..2}'
+ 1291311776.120680354 (5) echo 0
0
+ 1291311776.133917546 (3) for i in '{0..2}'
+ 1291311776.146386339 (5) echo 1
1
+ 1291311776.158646585 (3) for i in '{0..2}'
+ 1291311776.171003138 (5) echo 2
2
+ 1291311776.183450114 (7) sleep 1
+ 1291311777.203053652 (8) echo done
done
这假设GNU日期,但您可以将输出规范更改为您喜欢的任何内容或与您使用的日期版本匹配的任何内容。
注意:如果您希望在不修改现有脚本的情况下执行此操作,则可以执行以下操作:
PS4='+ $(date "+%s.%N ($LINENO) ")' bash -x scriptname
在即将推出的Bash 5中,您将能够保存分叉date
(但是你会得到微秒而不是纳秒):
PS4='+ $EPOCHREALTIME ($LINENO) '
答案 1 :(得分:10)
您可以将在-x
下运行的输出通过管道传输到收到每行时间戳的内容。例如,来自djb daemontools的tai64n
。
在一个基本的例子中,
sh -x slow.sh 2>&1 | tai64n | tai64nlocal
这会混淆stdout和stderr,但它确实为所有内容提供了时间戳。 然后,您必须分析输出以查找昂贵的行并将其关联回源。
您可能还会发现使用strace
很有帮助。例如,
strace -f -ttt -T -o /tmp/analysis.txt slow.sh
这将生成一份非常详细的报告,其中包含/tmp/analysis.txt
中的大量时间信息,但是在每个系统的呼叫级别上,这可能过于详细。
答案 2 :(得分:1)
听起来你想要为每个回声计时。如果回声就是你所做的一切,这很容易
alias echo='time echo'
如果你正在运行其他命令,这显然是不够的。
答案 3 :(得分:1)
我的首选方法如下。原因是它也支持OSX(没有高精度日期)&即使你没有安装bc也会运行。
#!/bin/bash
_profiler_check_precision() {
if [ -z "$PROFILE_HIGH_PRECISION" ]; then
#debug "Precision of timer is unknown"
if which bc > /dev/null 2>&1 && date '+%s.%N' | grep -vq '\.N$'; then
export PROFILE_HIGH_PRECISION=y
else
export PROFILE_HIGH_PRECISION=n
fi
fi
}
_profiler_ts() {
_profiler_check_precision
if [ "y" = "$PROFILE_HIGH_PRECISION" ]; then
date '+%s.%N'
else
date '+%s'
fi
}
profile_mark() {
_PROF_START="$(_profiler_ts)"
}
profile_elapsed() {
_profiler_check_precision
local NOW="$(_profile_ts)"
local ELAPSED=
if [ "y" = "$PROFILE_HIGH_PRECISION" ]; then
ELAPSED="$(echo "scale=10; $NOW - $_PROF_START" | bc | sed 's/\(\.[0-9]\{0,3\}\)[0-9]*$/\1/')"
else
ELAPSED=$((NOW - _PROF_START))
fi
echo "$ELAPSED"
}
do_something() {
local _PROF_START
profile_mark()
sleep 10
echo "Took $(profile_elapsed()) seconds"
}
答案 4 :(得分:0)
从here复制:
自从我到这里结束了至少两次之后,我实施了一个解决方案:
https://github.com/walles/shellprof
它运行您的脚本,透明地计时所有打印的行,最后显示屏幕上最长的行的前10个列表:
~/s/shellprof (master|✔) $ ./shellprof ./testcase.sh
quick
slow
quick
Timings for printed lines:
1.01s: slow
0.00s: <<<PROGRAM START>>>
0.00s: quick
0.00s: quick
~/s/shellprof (master|✔) $
答案 5 :(得分:0)
这里有一个简单的方法,几乎适用于所有 Unix,不需要特殊软件:
set -x
sh -x ./slow_script 2>&1 | logger
这会将输出写入系统日志,系统日志会自动为每条消息添加时间戳。如果您使用带有 journald 的 Linux,您可以使用
获得高精度时间戳journalctl -o short-monotonic _COMM=logger
许多传统的 syslog 守护进程还提供高精度时间戳(对于 shell 脚本来说,毫秒应该足够了)。
这是我刚刚以这种方式分析的脚本中的一个示例:
[1940949.100362] bremer root[16404]: + zcat /boot/symvers-5.3.18-57-default.gz
[1940949.111138] bremer root[16404]: + '[' -e /var/tmp/weak-modules2.OmYvUn/symvers-5.3.18-57-default ']'
[1940949.111315] bremer root[16404]: + args=(-E $tmpdir/symvers-$krel)
[1940949.111484] bremer root[16404]: ++ /usr/sbin/depmod -b / -ae -E /var/tmp/weak-modules2.OmYvUn/symvers-5.3.18-57-default 5.3.18-57>
[1940952.455272] bremer root[16404]: + output=
[1940952.455738] bremer root[16404]: + status=0
在这里您可以看到“depmod”命令花费了大量时间。
答案 6 :(得分:-3)