シェルスクリプト内の画面とファイルの両方にテキストを出力する方法は?


49

現在、次のようなログファイルにメッセージを記録するシェルスクリプトがあります。

log_file="/some/dir/log_file.log"
echo "some text" >> $log_file
do_some_command
echo "more text" >> $log_file
do_other_command

このスクリプトを実行するとき、画面への出力はありません。また、パテ経由でサーバーに接続しているため、別の接続を開いて「tail -f log_file_path.log」を実行する必要があります。スクリプトとリアルタイムで出力を見たい。

明らかに、私が望むのは、テキストメッセージが画面とファイルに印刷されることですが、2行ではなく1行で行いたいのです。1行にはファイルへのリダイレクトがありません。

これを達成する方法は?

回答:


71

これは動作します:

command | tee -a "$log_file"

tee入力をファイルに保存し(-a上書きではなく追加に使用)、入力を標準出力にもコピーします。


8

here-docとを使用できます。効率的でPOSIXに適した一般的なコレクターモデルを入手します。

. 8<<-\IOHERE /proc/self/fd/8

command
 
fn() { declaration ; } <<WHATEVER
# though a nested heredoc might be finicky
# about stdin depending on shell
WHATEVER
cat -u ./stdout | ./works.as >> expect.ed
IOHERE

ヒアドックを開くと、シェルにIOHERE入力トークンで信号を送り、リミッタートークンのもう一方の端に到達するまで、指定したファイル記述子に入力をリダイレクトする必要があります。私は見回しましたが、上で示したようにヒアドキュメント演算子と組み合わせてリダイレクトfd番号を使用する例は多く見ていませんが、その使用法はPOSIXの基本的なシェルコマンドガイドラインで明確に指定されています。ほとんどの人はstdinを指して撮影するだけですが、この方法でソーススクリプトレットを使用すると、stdinがフリーになり、構成アプリがブロックされたI / Oパスについて文句を言うことがなくなります。

ヒアドキュメントの内容は、指定したファイル記述子にストリームされ、次にシェルコードとして解釈され、によって実行されます。組み込み。ただし、特定のパスを指定せずに。。/ proc / selfパスで問題が発生する場合は、/ dev / fd / nまたは/ proc / $$を試してください。これと同じ方法は、パイプでも機能します:

cat ./*.sh | . /dev/stdin

おそらく、少なくとも見た目ほど賢くないでしょう。もちろん、shでも同じことができますが、。の目的は現在のシェル環境で実行することです。これはおそらくあなたが望んでいることであり、シェルによっては、heredocよりもはるかに動作しやすいです標準の匿名パイプを使用します。

とにかく、おそらくお気づきかもしれませんが、私はまだあなたの質問に答えていません。しかし、考えてみると、heredocがすべてのコードを。'sにストリームするのと同じように、単一の単純なアウトポイントも提供します。

. 5<<EOIN /dev/fd/5 |\ 
    tee -a ./log.file | cat -u >$(tty)
script
 
more script
EOIN

したがって、heredocで実行されたコードのすべての端末の標準出力は、からパイプアウトされます。もちろん、単一のパイプから簡単にティーオフできます。現在のstdoutの方向が不明なため、バッファなしのcat呼び出しを含めましたが、おそらく冗長で(ほぼ間違いなく書かれているとおりです)、パイプラインはおそらくティーで終了する可能性があります。

また、2番目の例で欠落しているバックスラッシュの引用を疑問視するかもしれません。この部分は、ジャンプインする前に理解することが重要であり、どのように使用できるかについていくつかのアイデアを提供するかもしれません。引用されたheredocリミッター(これまでIOHEREとEOINを使用し、最初にバックスラッシュで引用しましたが、「シングル」または「ダブル」引用は同じ目的に役立ちます)は、シェルでのパラメーター展開の実行を禁止しますただし、引用符で囲まれていないリミッターは、そのコンテンツを展開したままにします。あなたのヒアドキュメントがそうであるときのこれの結果。ソースは劇的です:

. 3<<HD ${fdpath}/3
: \${vars=$(printf '${var%s=$((%s*2))},' `seq 1 100`)} 
HD
echo $vars
> 4,8,12 
echo $var1 $var51
> 4 104

ヒアドキュメントリミッターを引用しなかったため、シェルは内容の読み取りを行い、結果のファイル記述子をに提供する前に内容を展開しました。実行する。これは本質的に、コマンドが2回解析されることになりました-とにかく拡張可能なコマンドです。バックスラッシュで$ varsパラメーター展開を引用したため、シェルは最初のパスでの宣言を無視し、バックスラッシュのみを削除したため、printf展開されたコンテンツ全体がnullの場合に評価できるようになりました。2番目のパスでスクリプトを入手しました。

この機能は、evalを使用するよりもheredocで引用する方がはるかに簡単であり、同様に危険である場合でも、組み込みの危険なevalシェルが提供できるものと基本的にまったく同じです。注意深く計画しない限り、習慣として「EOF」リミッターを引用するのがおそらく最善です。ただ言って。

編集:ええ、私はこれを振り返って、それは少し長すぎると思います。行う必要があるのが複数の出力を1つのパイプに連結することだけである場合、最も簡単な方法は以下を使用することです。

{ or ( command ) list ; } | tee -a ea.sy >>pea.sy

カーリーは現在のシェルでコンテンツを実行しようとしますが、括弧は自動的にサブアウトします。それでも、誰でもあなたにそれを伝えることができ、少なくとも私の意見では、。特にシェルが実際にどのように機能するかを理解したい場合、heredocソリューションははるかに貴重な情報です。

楽しんで!


3

インストールログを書き込むためにインストールスクリプトを変更しようとしたときに、この回答が見つかりました。

私のスクリプトはすでに次のようなechoステートメントでいっぱいです。

echo "Found OLD run script $oldrunscriptname"
echo "Please run OLD tmunsetup script first!"

そして、teeステートメント(または、teeを使用して既存のステートメントを呼び出す別のスクリプト)を実行したくないので、これを書きました。

#!/bin/bash
# A Shell subroutine to echo to screen and a log file

LOGFILE="junklog"

echolog()
(
echo $1
echo $1 >> $LOGFILE
)


echo "Going"
echolog "tojunk"

# eof

元のスクリプトで、「echo」を「echolog」に変更するだけで、ログファイルに出力することができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.