• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:【sh】tee コマンドの代替方法)

パイプを使わずにteeコマンドと同じように振る舞わせる方法は?

このQ&Aのポイント
  • 関数の標準出力をリアルタイムにターミナルに表示しながら、ログファイルにも保存する方法はありますか?現在は暫定的にtailコマンドを使用しているが、killコマンドを実行する際に出力されるメッセージが消せないため改善したい。
  • シェルスクリプトで関数の出力をターミナルに表示し、同時にログファイルに保存する方法を教えてください。現在はtailコマンドを使用していますが、killコマンドで出力されるメッセージが消えずに残ってしまいます。
  • teeコマンドの代替方法として、関数の標準出力をリアルタイムにターミナルに表示しながら、ログファイルにも保存する方法を教えてください。現在はtailコマンドを使用していますが、killコマンドで出力されるメッセージが削除できません。

質問者が選んだベストアンサー

  • ベストアンサー
  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.2

最初意味がわからなかったのですが function func { a=5 echo $a } a=0 echo $a #→ 0 func | tee -a ファイル # ファイルに「5」 echo $a #→ 上のパイプが無いと5,あると0 ←ある場合でも5にしたい ということでよろしいでしょうか? 思いつくのは次のものです。 やりたいことと一致しないかもしれませんが。 ・関数内で、teeを使う function func { a=5 echo $a | tee -a ファイル } ・メインルーチンを()でサブシェルにして、全体にteeを使う (a=0 echo $a func echo $a ) | tee -a ファイル ・メインルーチンを関数にして、その関数にteeを使う function main { a=0 echo $a func echo $a } main | tee -a ファイル ・コマンドラインでteeを使う bash script.sh | tee -a ファイル

tsuduki123
質問者

お礼

teeを使っていたのは 最終的に画面とログファイルに出力結果が出ていればよいだけなので ループの根元で一カ所だけteeするようにするのが一番無難に思えてきました。

tsuduki123
質問者

補足

パイプを使わずにteeをしているときと同じことをしたいというのが目的なのですが その背景としては、作成しているスクリプトでは、 とあるディレクトリ内にあるファイルを順次読み込んで、決まった名前の関数を実行するという形になっています。(この関数にはテストが書いてあるので以下テストといいます。) tee を利用しているのは、テストが出力した結果を画面でリアルタイムに確認できるようにするのと あとで、確認できるようにログファイルにも同時に保存するためにtee を利用していました。 そして、このテストの結果を$PIPESTATUS で判断していたのですが、 exitコードだけでなく、テストの内部で発生したエラーの履歴をスタックしていきたいと思ったことが発端です。 なので、やりたいことを全部列挙するならば 1. 実行している関数の画面出力をリアルタイムに確認したい 2. 順次実行した関数の画面出力をログファイルに残したい 3. 実行した関数の中で発生したエラーの履歴をexitコードとともに取得したい ということでしょうか。 んで、 3. を実現するためには パイプは使えないので tee が使えなくなり tee が使えないと1. と 2.が機能しないということで困っていました。

その他の回答 (3)

  • dscripty
  • ベストアンサー率51% (166/325)
回答No.4

書き換えたいシェル変数が exit ステータスの範囲なら、…… $ function some_func () { > echo $STATUS > return $(expr $STATUS - 1) > } $ export STATUS=5 $ while [ $STATUS != 0 ]; do > some_func > export STATUS=$? > done | tee log.txt 5 4 3 2 1 $ cat log.txt 5 4 3 2 1 $

tsuduki123
質問者

お礼

すみません。 今回はexitコードとは別にもうひとつステータスコードを取得したいということもあるので この方法は使えません。

  • notnot
  • ベストアンサー率47% (4900/10358)
回答No.3

補足1に書かれた物レベルだと、いろいろ手はありますね。条件に合う物を選んでください。 案1:安易な方法 ( STATUS=120 test () { STATUS=130 echo $STATUS #リダイレクト用と端末表示用に二回出す echo $STATUS >&2 } echo in:${STATUS} >&2 test echo out:${STATUS} >&2 ) >> a.log 案2:質問に書かれた方法で、killのメッセージを抑止 STATUS=120 test () { STATUS=130 echo $STATUS } tail -f a.log & echo in:${STATUS} test >> a.log echo out:${STATUS} sleep 2 exec 2>/dev/null #メッセージを捨てる kill %1 sleep 1 # ちょっと待つ exec 2>/dev/tty # 元に戻す。後続処理がないなら不要 案3:パイプ内から外側の変数を書き換える方法 touch tmpfile trap ". tmpfile" SIGUSR1 #シグナルを受けるとファイルの中身を自プロセスで実行 STATUS=120 test () { STATUS=130 echo STATUS=$STATUS > tmpfile #実行して欲しいコマンドを書く kill -SIGUSR1 $$ #書いたファイルを親プロセスに実行させる echo $STATUS } echo in:${STATUS} test | tee -a a.log echo out:${STATUS} 最後のが応用範囲が広いですが、ファイルを使うので後始末や名前の重複の考慮が必要。例えば WORK=/tmp/tmpfile$$ touch $WORK trap "rm $WORK" 0 1 2 3 4 5 6 7 8 11 12 13 14 15 trap ". $WORK" SIGUSR1 STATUS=120 test () { STATUS=130 echo STATUS=$STATUS > $WORK kill -SIGUSR1 $$ echo $STATUS } echo in:${STATUS} test | tee -a a.log echo out:${STATUS} なお、testというのはシェルの組み込みコマンドにも/usr/binにもあるので例示としても避けた方が良いです。

tsuduki123
質問者

お礼

test については失礼しました。 何も考えず書いて予定通り動いたからそのまま貼り付けてしまいました。 ファイルを使う方法は最後の手段として考えたいと思います。 実行時には、実行単位にワークディレクトリを作成しているので後しまつは考えなくてもよいようになっています。 # むしろ、作成したファイルは一切削除しない方針で作成しています。 killメッセージの抑止については sh -c "tail -f ログファイル名 & echo \$! > $$.tail.pid" kill -TERM `cat $$.tail.pid` という感じで抑止するようにしました。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.1

うん? 「シェル変数を書き換える」ことと「パイプが使えない」こととの関係が理解できないんだけど....

tsuduki123
質問者

補足

shの パイプは子プロセスとして実行されるため、環境を呼び出し元へ引き継ぐことができません。 あれです。 cat file | while read a; do ; done の形式でファイルを読み込みながらカウントしたのに カウント結果がとれずはまるっていうあれです。 実際のスクリプトは配列を使っているのですが $ sh ./a.sh in:120 130 out:120 という出力は ---- export STATUS=120 test () { export STATUS=130 echo $STATUS } echo in:${STATUS} test | tee -a a.log echo out:${STATUS} ---- というshから出力された結果です。

関連するQ&A

専門家に質問してみよう