アプリ版:「スタンプのみでお礼する」機能のリリースについて

お世話になります。
パイプを使わずにtee コマンドと同じように振る舞わせる方法がわかりません。
なにかよい知恵はありませんでしょうか。。。

今まで、bashスクリプトの中で
関数 | tee -a ログファイル名
という形式でログファイルを出力していたのですが
関数の中でシェル変数を書き換える必要があってパイプが使えなくなってしまいました。

リアルタイムに関数の標準出力をターミナル出力しつつ、ログファイルにも保存していきたいのですが
こういう場合、どうすればよいのでしょうか?

とりあえず
touch ログファイル名
tail -f ログファイル名 &
関数
sleep 2
kill %1

という形で、暫定的に逃げているのですが
kill したときに出力されるメッセージが消せずにかっこわるいので
できればこの方法は変更したいです。

ご教示ください。

A 回答 (4件)

最初意味がわからなかったのですが



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 ファイル

この回答への補足

パイプを使わずにteeをしているときと同じことをしたいというのが目的なのですが
その背景としては、作成しているスクリプトでは、
とあるディレクトリ内にあるファイルを順次読み込んで、決まった名前の関数を実行するという形になっています。(この関数にはテストが書いてあるので以下テストといいます。)

tee を利用しているのは、テストが出力した結果を画面でリアルタイムに確認できるようにするのと
あとで、確認できるようにログファイルにも同時に保存するためにtee を利用していました。

そして、このテストの結果を$PIPESTATUS で判断していたのですが、
exitコードだけでなく、テストの内部で発生したエラーの履歴をスタックしていきたいと思ったことが発端です。

なので、やりたいことを全部列挙するならば
1. 実行している関数の画面出力をリアルタイムに確認したい
2. 順次実行した関数の画面出力をログファイルに残したい
3. 実行した関数の中で発生したエラーの履歴をexitコードとともに取得したい

ということでしょうか。
んで、 3. を実現するためには パイプは使えないので tee が使えなくなり
tee が使えないと1. と 2.が機能しないということで困っていました。

補足日時:2012/05/09 20:51
    • good
    • 0
この回答へのお礼

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

お礼日時:2012/05/09 21:09

書き換えたいシェル変数が 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
$
    • good
    • 0
この回答へのお礼

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

お礼日時:2012/05/09 21:28

補足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にもあるので例示としても避けた方が良いです。
    • good
    • 0
この回答へのお礼

test については失礼しました。
何も考えず書いて予定通り動いたからそのまま貼り付けてしまいました。

ファイルを使う方法は最後の手段として考えたいと思います。
実行時には、実行単位にワークディレクトリを作成しているので後しまつは考えなくてもよいようになっています。
# むしろ、作成したファイルは一切削除しない方針で作成しています。

killメッセージの抑止については
sh -c "tail -f ログファイル名 & echo \$! > $$.tail.pid"
kill -TERM `cat $$.tail.pid`
という感じで抑止するようにしました。

お礼日時:2012/05/09 21:00

うん?



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

この回答への補足

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から出力された結果です。

補足日時:2012/05/09 00:43
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!