現在、次の設定を使用して複数のコマンドの出力をリダイレクトします。
echo "Some normal commands"
(
echo "Error: something happened"
echo "Warning: this incident will be logged"
) >> logfile
echo "More normal commands"
これは非常に便利で、パイプでも機能します。
これが最善の方法ですか?考慮すべき他のオプションはありますか?
答え1
別の方法は、括弧の代わりに中括弧を使用することです。この変更を実行するコマンド現在のシェル、そこにいないサブシェル
echo "Some normal commands"
{
echo "Error: something happened"
echo "Warning: this incident will be logged"
} >> logfile
echo "More normal commands"
引用:https://www.gnu.org/software/bash/manual/bashref.html#Command-Grouping
これは、グループ内で変数を変更する場合に特に重要です。
$ x=5; ( x=10; echo inside: $x; ); echo outside: $x
inside: 10
outside: 5
$ x=5; { x=10; echo inside: $x; }; echo outside: $x
inside: 10
outside: 10
答え2
Glennの答えは素晴らしいです。( ... )
との違いが{ ... }
重要です。
あなたの質問などのエラー出力によく使用される戦略の1つはCommandsですtee
。次のことができます。
echo "Normal output"
{
printf "[%s] %s\n" "$(date '+%Y-%m-%d %T')" "Warning text"
printf "[%s] %s\n" "$(date '+%Y-%m-%d %T')" "This event is logged."
} | tee -a $logfile >&2
echo "More normal output"
このtee
コマンドは出力を2つの場所に送信します。-a
「append」オプションは指定されたファイルに出力を送信し、コマンドは入力も標準出力に渡します。>&2
行末でstdoutをtee
stderrにリダイレクトし、これは異なる方法で処理できます(たとえば、cronジョブなど)。
シェルスクリプトでよく使用されるもう1つの方法は、スクリプトが端末で実行されているかオプションが提供されているかに応じて、デバッグの動作や詳細な出力を変更することです-v
。たとえば、
#!/bin/sh
# Set defaults
if [ -t 0 ]; then
Verbose=true; vflag="-v"
else
Verbose=false; vflag=""
fi
Debug=false; AskYN=true; Doit=true
# Detect options (altering defaults)
while getopts vdqbn opt; do
case "$opt" in
v) Verbose=true; vflag="-v" ;; # Verbose mode
d) Debug=true; Verbose=true; vflag="-v" ;; # Very Verbose
q) Verbose=false; vflag="" ;; # quiet mode (non-verbose)
b) AskYN=false ;; # batch mode
n) Doit=false ;; # test mode
*) usage; exit 1 ;;
esac
done
# Shift our options for further processing
shift $(($OPTIND - 1))
$Verbose && echo "INFO: Verbose output is turned on." >&2
$Debug && echo "INFO: In fact, expect to be overrun." >&2
# Do your thing here
if $AskYN; then
read -p "Continue? " choice
case "$choice" in
Y|y) $Doit && somecommand ;;
*) echo "Done." ;;
esac
fi
スクリプトは上部にこのような一般的な内容から始めることができ、詳細とデバッグ出力はスクリプト全体に分散されています。これはただ一つのアプローチです。アプローチはさまざまであり、人々はこれらの問題にアクセスする独自の方法を持っています。特にしばらく使っていたらもっとそうです。 :)
別のオプションは、よりインテリジェントなタスクを実行できるシェル機能である「ハンドラー」を使用して出力を処理することです。たとえば、
#!/bin/bash
logme() {
case "${1^^}" in
[IN]*) level=notice ;;
W*) level=warning ;;
A*) level=alert ;;
E*) level=emerg ;;
*) level=notice ;;
esac
if [[ "$#" -eq 1 ]]; then
# Strip off unnecessary prefixes like "INFO:"
string="${1#+([A-Z])?(:) }"
else
shift
string="$@"
fi
logger -p "${facility}.${level}" -t "$(hostname -s)" "$string"
}
echo "Normal output"
logme INFO "Here we go..."
somecommand | logme
echo "Additional normal output"
(注、${var^^}
bash専用です。)
これにより、システムsyslog
機能を使用できるシェル関数が生成されます(logme()`logger
コマンドを使用) to send things to system logs. The
)。この関数は、ログデータの1行を生成するオプションと組み合わせて使用することも、stdinで処理される複数行の入力と組み合わせて使用することもできます。魅力的に見えるために使用することができます。
これははいそして、あなたがそれを理解し、それがあなたに必要なものであることを正確に知らない限り、逐語的にコピーしてはいけません。より良いアイデアは、ここで概念を取り、独自のスクリプトで直接実装することです。
答え3
より適切なアプローチは{ command; }
代わりに使用することです(command)
。その理由は、コマンドがサブシェル()
にグループ化されると、コマンドを実行するためにサブシェルが開かれるため、ブロック内で初期化された変数はスクリプトの他の部分で使用できないためです。
対照的に、コマンドのグループ化を使用すると、コマンドは{}
同じシェルで実行されるため、スクリプトの他の部分で変数を使用できます。
echo "Some normal commands"
{
var=1
echo "Error: something happened"
echo "Warning: this incident will be logged"
} >> logfile
echo "The value of var is: $var"
echo "More normal commands"
ここでこの部分が実行されると、$var
変数はその値を保持しますが、他の場合はそうではありません。