2013-08-04 88 views
6

我有一个bash脚本,它需要将近5秒的时间才能运行。我想调试它,并确定哪些命令花费的时间最长。这样做的最好方法是什么?有没有可以设置的标志?设置#!/bin/bash -vx并没有真正的帮助。我想要的基本上是按行号执行的时间。如何调试bash脚本并获取每个命令的执行时间

+0

你为什么这么不耐烦?在运行脚本时花费一个小时来削减奇数秒? –

+2

该脚本运行频率很高,需要对其进行优化。 – Justin

+1

经常多久?它的速度是很重要的,也许用C++编写(例如)是一个更好的选择。除此之外为什么不发布脚本? –

回答

2

您可以使用set -x让脚本在执行前打印每个命令。我不知道如何获得自动添加的命令时序。您可以在整个脚本中使用date命令来标记时间。

5

可以使用time工具来衡量你的个人命令/功能的运行时间。

例如:

[[email protected] ~]$ cat times.sh 
#!/bin/bash 

test_func() 
{ 
    sleep 1 
    echo "test" 
} 

echo "Running test_func()" 
time test_func 
echo "Running a 5 second external command" 
time sleep 5 

运行在类似下面这个脚本的结果:

[[email protected] ~]$ ./times.sh 
Running test_func() 
test 

real 0m1.003s 
user 0m0.001s 
sys  0m0.001s 
Running a 5 second external command 

real 0m5.002s 
user 0m0.001s 
sys  0m0.001s 
+0

不要使用'test'作为函数名称。这是一个内置的外壳。重命名为'test_time'或其他... – anishsane

+0

感谢您的评论。固定。 – Ben

+1

nitpick:'time'默认为_shell keyword_(同时也作为外部工具存在,'/ usr/bin/time')。只有shell关键字能够测量任意shell命令_as a whole_(比较从'time ls | sleep 1'到'/ usr/bin/time ls | sleep 1'的输出)。 – mklement0

1

试试这个:

sed 's/^\([^#]\)/time \1/' script.sh>tmp.sh && ./tmp.sh 

它预先考虑时间命令将所有非命令行

+4

这并不总是奏效:考虑'我在{1..3} \ ndo \ necho $ i \ ndone'中。一些简单的事情,但是“时间做”和“完成时间”无效 – SheetJS

+0

这是事实,但它至少允许一定程度的自动化。我喜欢这个想法,但它必须得到改进...... – icedwater

9

这是尽可能接近内置bash调试工具的答案,因为它可以从脚本执行开始时间提供整体计时信息。

在脚本的顶部添加此为第二计:

export PS4='+[${SECONDS}s][${BASH_SOURCE}:${LINENO}]: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'; set -x; 

相同,但用毫秒来代替:

N=`date +%s%N`; export PS4='+[$(((`date +%s%N`-$N)/1000000))ms][${BASH_SOURCE}:${LINENO}]: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'; set -x; 

的最后一个例子可以到微秒级精度,只要记住你正在使用bash :)。

Exampe脚本:

#!/bin/bash 
N=`date +%s%N` 
export PS4='+[$(((`date +%s%N`-$N)/1000000))ms][${BASH_SOURCE}:${LINENO}]: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'; set -x; 
sleep 1 
exit 

实施例的调试输出:

+[3ms][/root/db_test.sh:5]: sleep 1 
+[1012ms][/usr/local/bin/graphite_as_rand_stat.sh:6]: exit 

请注意,可以有选择地通过“设置-x”包围它调试脚本的特定部分在在调试结束时调试开始和'调试+ x'。计时数据仍将显示从执行开始正确计数。

附录

为了完整起见,如果你需要的差分时钟数据可以重定向调试信息到一个文件,随后对其进行处理。

鉴于这种示例脚本:

#!/bin/bash 
N=`date +%s%N` 
export PS4='+[$(((`date +%s%N`-$N)/1000000))ms][${BASH_SOURCE}:${LINENO}]: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'; set -x; 
sleep 1 
for ((i=0;i<2;i++)); do 
     o=$(($RANDOM*$RANDOM/$RANDOM)) 
     echo $o 
     sleep 0.$o 
done 
exit 

运行它,而重定向调试到一个文件:

./example.sh 2>example.dbg 

和输出与此差动调试定时(覆盖多线):

p=0; cat example.dbg | while read l; do [[ ! ${l%%[*} =~ ^\+ ]] && echo $l && continue; i=`echo $l | sed 's#[^0-9]*\([0-9]\+\).*#\1#'`; echo $l | sed "s#${i}ms#${i}ms+$(($i-$p))ms#"; p=$i; done 

输出:

+[2ms+2ms][./example.sh:5]: sleep 1 
+[1006ms+1004ms][./example.sh:6]: ((i=0)) 
+[1009ms+3ms][./example.sh:6]: ((i<2)) 
+[1011ms+2ms][./example.sh:7]: o=19258 
+[1014ms+3ms][./example.sh:8]: echo 19258 
+[1016ms+2ms][./example.sh:9]: sleep 0.19258 
+[1213ms+197ms][./example.sh:6]: ((i++)) 
+[1217ms+4ms][./example.sh:6]: ((i<2)) 
+[1220ms+3ms][./example.sh:7]: o=176 
+[1226ms+6ms][./example.sh:8]: echo 176 
+[1229ms+3ms][./example.sh:9]: sleep 0.176 
+[1442ms+213ms][./example.sh:6]: ((i++)) 
+[1460ms+18ms][./example.sh:6]: ((i<2)) 
+[1502ms+42ms][./example.sh:11]: exit 
+0

非常好地完成。这个问题被标记为'linux',但由于这些技术在支持bash的所有平台上都可能有用,所以让我补充一点:可悲的是,'%N'在BSD/OSX上不能与'date'一起工作,所以毫秒变体doesn在那里工作。 – mklement0

+0

毫秒变体也是这样做的,但是为了也从'set -x'运行的位置开始测量'$ SECONDS'变体,首先执行'SECONDS = 0'。最后,值得一提的是,测量自身的行为会稍微增加执行时间,而'$ SECONDS'方法则更少。 – mklement0

+0

非常好的答案。另见http://stackoverflow.com/questions/5014823/how-to-profile-a-bash-shell-script –