呼び出し時に渡すコマンドライン引数に基づくさまざまなifステートメントを含むbashスクリプトがあります。どのコマンドが実行されているかについて何らかの出力があると、それらすべてのifステートメントのフローを確認するのに役立ちますが、現在の解決策では、多すぎる情報が得られます。
スクリプトでset -v
を使用すると、スクリプトで実行されたときに画面に出力されるコマンドを確認するのに多少役立ちましたが、多くのコマンドがtoo表示されます。まるでスクリプト全体のコピーのようなものです。
実行されているコマンドを示す出力が必要ですが、コメント、改行、ifステートメントの式などは表示したくありません。
印刷する前に、まず-vオプションで生成されたすべての可能な出力を正規表現に渡す方法はありますか?または、特定の「タイプ」の出力コマンドのみをbashに取得するための他の解決策(たとえば、実行可能ファイルを使用していて、特定のステートメント、コメントなどだけではない)?
[1] https://stackoverflow.com/questions/257616/Sudo-changes-path-why はこの点で非常に役立ち、set -v
の使用法に関する提案を得た場所です。
編集:
私が実行しているものと同様の(ただし同一ではない)スクリプト:
#!/bin/bash
#get verbose command output
set -v
env=$1
if [ "$env" == "dev" ]; then
python ascript.py
fi
if [ "$env" == "prod" ]; then
#launching in prod will most likely fail if not run as root. Warn user if not running as root.
if [ $EUID -ne 0 ]; then
echo "It doesn't look like you're running me as root. This probably won't work. Press any key to continue." > /dev/stderr
read input
fi
#"stop" any existing nginx processes
pkill -f nginx
nginx -c `pwd`/conf/artfndr_nginx.conf
fi
このスクリプトから出力行の可能なセットが2つだけ必要です。最初:
python ascript.py
二番目:
pkill -f nginx
nginx -c /some/current/directory/conf/artfndr_nginx.conf
より複雑なbashスクリプトを作成する場合、コマンドを実行するための小さな関数を使用して、実行されたコマンドをログファイルに出力します。
runthis(){
## print the command to the logfile
echo "$@" >> $LOG
## run the command and redirect it's error output
## to the logfile
eval "$@" 2>> $LOG
}
次に、スクリプトで次のようなコマンドを実行します。
runthis "cp /foo/bar /baz/"
コマンドを出力したくない場合は、通常どおりに実行してください。
$LOG
ファイル名に変換するか、ファイル名を削除してstdoutまたはstderrに出力します。
サブシェルを使用します。つまり、
( set -x; cmd1; cmd2 )
例えば:
( set -x; echo "hi there" )
プリント
+ echo 'hi there'
hi there
@terdonと同様に使用されるメソッドを見てきました。これは、より高いレベルのプログラミング言語がロガーを呼び出し、log4J(Java)、log4Perl(Perl)などの本格的なライブラリとして提供するものの始まりです。
前述のように、Bashでset -x
を使用しても同様の結果が得られますが、コードブロックをそのようにラップすることで、コマンドのサブセットのみをデバッグするために使用できます。
$ set -x; cmd1; cmd2; set +x
これは、使用できる1つのライナーパターンです。
$ set -x; echo "hi" ;set +x
+ echo hi
hi
+ set +x
スクリプト内の複数のコマンドに対して、このようにラップできます。
set -x
cmd1
cmd2
set +x
cmd3
ほとんどの人は気づいていませんが、Bashにもlog4 * Log4Bash があります。より控えめなニーズがある場合は、これを設定する価値はあります。
log4bashは、Bashスクリプトのログを向上させるための試みです(つまり、Bashでのログの記録を少なくします)。
Log4bashの使用例をいくつか示します。
#!/usr/bin/env bash
source log4bash.sh
log "This is regular log message... log and log_info do the same thing";
log_warning "Luke ... you turned off your targeting computer";
log_info "I have you now!";
log_success "You're all clear kid, now let's blow this thing and go home.";
log_error "One thing's for sure, we're all gonna be a lot thinner.";
# If you have figlet installed -- you'll see some big letters on the screen!
log_captains "What was in the captain's toilet?";
# If you have the "say" command (e.g. on a Mac)
log_speak "Resistance is futile";
私がlog4 *フレームワークのフルパワーのより多くに分類するものを望むなら、私は Log4sh を試してみます。
抜粋
log4shはもともと、私が作業している実稼働環境の一部で発生していたロギングの問題を解決するために開発されました。特にCronの仕事は、すべてがうまくいった、または何もうまくいかなかったが詳細な理由ではないことを伝える彼らの絶え間なく迷惑なメールで私に最も頭痛を引き起こしました。シェルスクリプトからのロギングが重要な環境でlog4shを使用していますが、単純な「Hello、fix me!」以上のものが必要です。ロギングメッセージのタイプ。気に入った点や改善点がありましたら、遠慮なくメールでお知らせください。プロジェクトに十分な関心があれば、さらに開発します。
log4shはLinuxのBourne Again Shell(/ bin/bash)の下で開発されましたが、SolarisのデフォルトのBourne Shell(/ bin/sh)で動作するように細心の注意が払われています。自分が使用するプラットフォーム。
Log4shは、Bashだけでなく、いくつかのシェルをサポートしています。
Linuxだけでなく、いくつかのOSでもテストされています。
Log4 *フレームワークを使用することは学ぶのに少し時間がかかりますが、ロギングからより厳しいニーズがある場合は価値があります。 Log4shは、アペンダーを定義し、表示される出力のフォーマットを制御できる構成ファイルを使用します。
#! /bin/sh
#
# log4sh example: Hello, world
#
# load log4sh (disabling properties file warning) and clear the default
# configuration
LOG4SH_CONFIGURATION='none' . ./log4sh
log4sh_resetConfiguration
# set the global logging level to INFO
logger_setLevel INFO
# add and configure a FileAppender that outputs to STDERR, and activate the
# configuration
logger_addAppender stderr
appender_setType stderr FileAppender
appender_file_setFile stderr STDERR
appender_activateOptions stderr
# say Hello to the world
logger_info 'Hello, world'
今私がそれを実行すると:
$ ./log4sh.bash
INFO - Hello, world
注:上記は、コードの一部としてアペンダーを構成します。これが好きなら、独自のファイル、log4sh.properties
などに抽出できます。
詳細が必要な場合は Log4shの優れたドキュメント を参照してください。
あなたはtrap
DEBUG
をテストして the BASH_COMMAND
変数 。これをスクリプトの先頭に追加します。
log() {
case "$1" in
python\ *)
;&
pkill\ *)
printf "%s\n" "$*"
;;
esac
}
trap 'log "$BASH_COMMAND"' DEBUG
コードは読み取り可能です。最初の引数がpython
またはpkill
で始まるかどうかをテストし、そうであればそれを出力します。そして、トラップはBASH_COMMAND
(実行されるコマンドを含む)を最初の引数として。
$ bash foo.sh dev
python ascript.py
python: can't open file 'ascript.py': [Errno 2] No such file or directory
$ bash foo.sh prod
It doesn't look like you're running me as root. This probably won't work. Press any key to continue.
pkill -f nginx
foo.sh: line 32: nginx: command not found
case
はグロブを使用しますが、同じように簡単に実行できます。
if [[ $1 =~ python|nginx ]]
then
printf "%s" "$*"
fi
正規表現を使用します。
これは、Steven Pennyのきちんとした機能の改訂版です。引数をカラーで表示し、必要に応じて引用します。これを使用して、トレースするコマンドを選択的にエコーします。引用符が出力されるため、印刷された行をコピーしてターミナルに貼り付け、スクリプトのデバッグ中に即座に再実行できます。最初のコメントを読んで、私が何を変更したのか、その理由を知ってください。
_xc() # $@-args
{
cecho "$@"
"$@"
}
cecho() # $@-args
{
awk '
BEGIN {
x = "\047"
printf "\033[36m"
while (++i < ARGC) {
if (! (y = split(ARGV[i], z, x))) {
printf (x x)
} else {
for (j = 1; j <= y; j++) {
printf "%s", z[j] ~ /[^[:alnum:]%+,./:=@_-]/ ? (x z[j] x) : z[j]
if (j < y) printf "\\" x
}
}
printf i == ARGC - 1 ? "\033[m\n" : FS
}
}
' "$@"
}
_
出力の使用例:
_# xc echo "a b" "c'd" "'" '"' "fg" '' " " "" \# this line prints in green
_
_echo 'a b' c\'d \' '"' fg '' ' ' '' '#' this line prints in green
_
_a b c'd ' " fg # this line prints in green
_
上の2行目は緑色で印刷され、コピーして貼り付けて3行目を再現できます。
補足
@ Steven-Pennyのオリジナルのxcは賢いので、彼はすべてのクレジットに値します。ただ、気づいた点もいくつかありますが、評判が悪いため、直接コメントすることはできませんでした。それで私は彼の投稿に提案された編集を加えましたが、査読者は私の編集を拒否しました。したがって、私はこの回答としてコメントを投稿することにしましたが、スティーブペニー自身の回答を編集できるようにしたいと思いました。
Steven-Pennyの回答に対して変更したもの
修正済み:null文字列の印刷-それらは印刷されませんでした。修正済み:_%
_を含む文字列の印刷-awk構文エラーが発生していました。 for (j in ...)
をfor (j = 0, ...)
に置き換えました。前者は配列トラバーサルの順序を保証しないためです(awkは実装に依存します)。移植性のために8進数に0を追加しました。
更新
Steven Penny'sはその後、彼の回答でこれらの問題を修正したため、これらの発言は私の回答の歴史的な記録のみに留まります。詳細については、コメントセクションを参照してください。
POSIXの「sh_trace」シェル関数 stdlibライブラリ を使用して、コマンドを実行する前にカラーで印刷できます。例:
基本的なAwk関数:
function sh_trace(ary, b, d, k, q, w, z) {
b = "\47"
for (d in ary) {
k = split(ary[d], q, b)
q[1]
if (d - 1)
z = z " "
for (w in q) {
z = z (!k || q[w] ~ "[^[:alnum:]%+,./:=@_-]" ? b q[w] b : q[w]) \
(w < k ? "\\" b : "")
}
}
printf "\33[36m%s\33[m\n", z
system(z)
}