2013-08-04 3 views
6

私はbashスクリプトを実行するのに約5秒かかる。私はそれをデバッグし、どのコマンドが最長の時間を取っているかを判断したいと思います。これを行う最善の方法は何ですか?私が設定できるフラグはありますか? #!/bin/bash -vxの設定は本当に役に立ちません。私が欲しいのは、基本的に行番号による実行時間です。bashスクリプトをデバッグしてコマンドごとの実行時間を取得する方法

+0

どうしてそんなに気楽ですか?スクリプトを実行するときに奇妙な秒を切るために1時間を費やすつもりですか? –

+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

nitick: 'time'はデフォルトで_shellキーワードです(外部ユーティリティ'/usr/bin/time'としても存在します)。シェルのキーワードだけが任意のシェルコマンドを測定することができます_全体として( 'time ls | sleep 1'から'/usr/bin/time ls | sleep 1'への出力を比較してください)。 – mklement0

1

このお試しください:

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

それはすべての非にtimeコマンドを付加しますコマンドライン

+4

これは必ずしもうまくいかない: '{1..3} \ ndo \ necho $ i \ ndone'の'を考えてください。何かシンプルですが、 'time do'と' time done'は無効です – 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設定」で囲むことで、スクリプトの特定の部分をデバッグすることができますに注意してくださいにデバッグの開始時に 'debug + 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

非常にうまくやった。 BSD/OSXの 'date'では'%N'は動作しませんので、ミリ秒の変種はありません。この問題は、 'linux'とタグ付けされていますが、このテクニックはbashをサポートするすべてのプラットフォームで役に立つ可能性があるので、そこで働きません。 – mklement0

+0

ミリ秒の変形もこれを行いますが、 'set -x'が実行された時点から測定を開始する' $ SECONDS'変種を取得するには、まずSECONDS = 0を実行してください。最後に、 '$ SECONDS'アプローチでは、測定時間がわずかに増えることになります。 – mklement0

+0

非常に良い答え。 http://stackoverflow.com/questions/5014823/how-to-profile-a-bash-shell-scriptも参照してください。 –

関連する問題