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

結果を標準出力に出すシェルスクリプトを、">"で出力先をファイルにリダイレクトして動かすと、(時間のかかる処理の間に出力ファイルサイズを見ると)増えていくはずのファイルサイズが途中で減ることがあります。
シェルスクリプト内には、csvファイルを1行読み込んでは整形出力するループ処理のrubyスクリプトがあり、ループの中、及びループの前後に標準出力があります。
シェルスクリプト内の複数のコマンド標準出力はスクリプトが終了するまで1つの書き出しになると思っていますが、いったんクローズされて次の書き込みと扱われる場合があるのでしょうか?

質問者からの補足コメント

  • うーん・・・

    問題としているスクリプトの構造を単純化したものとして、次のスクリプトを走らせると
    出力ファイルには最後に実行された結果しか記録されていませんでした。
    hoge.rb出力リダイレクトを ">>" にするとすべて記録されていました。
    どういう場合に、実行中のスクリプトの標準出力が閉じられるのでしょうか?

    (foo.sh)
    #!/bin/bash
    for i in 1 2 3 4 5 ; do
    ruby hoge.rb $i > hoge-out.txt
    done


    (hoge.rb)
    #!/usr/bin/env ruby
    puts ARGV[0]

      補足日時:2018/08/19 15:40

A 回答 (11件中1~10件)

> 次のスクリプトtmp.shでは、standard-out-from-tmp2sh.txtにtmp2.shの2つのlsコマンドの結果が書き込まれます。


> これは違う事情なのでしょうか?
>
> (tmp.sh)
> #!/bin/bash
> ScriptName=$(basename $0)
> tmp2.sh > standard-out-from-tmp2sh.txt
>
> (tmp2.sh)
> #!/bin/bash
> ls | sed ''
> ls | sed 's/^/HI/

tmp2.sh では ls を二回実行してますよね。
対して

> (foo.sh)
> #!/bin/bash
> for i in 1 2 3 4 5 ; do
> ruby hoge.rb $i > hoge-out.txt
> done
>
> (hoge.rb)
> #!/usr/bin/env ruby
> puts ARGV[0]

hoge.rb では puts を一回実行しているだけ。
だから一つの結果しか残らない。

条件の違うものを対象にして動作が違うと言われても当たり前のこと。
試しに tmp.sh 内の tmp2.sh 実行個所をコピペして二、三回実行してみると動きの違いも分かるんじゃないの。
2つの ls の結果しか残らないよ。

それと確認雑にしていた。
No,6 に書かれてるように echo /dev/null は間違ってたね。
cat /dev/null だね。
    • good
    • 0
この回答へのお礼

f02e様、ありがとうございます。
「シェルスクリプトの中でコマンドAを呼び出し、コマンドAの中で上書き標準出力リダイレクトが複数行われるとファイルに追加される。
シェルスクリプトの中でコマンドAとコマンドBを呼び出して、各コマンドが同じファイルに上書き標準出力をリダイレクトすると、最後の出力だけが記録される」ということですね。
前段が理解できないです。

お礼日時:2018/08/23 23:15

う~ん, 言葉の定義からやりなおしかなぁ.



例えば https://linuxjm.osdn.jp/html/GNU_bash/man1/bash. … だと,「出力リダイレクト」は「[n]>word」という形だっていってる. つまり
/bin/sh tmp2.sh > tmp-out.txt
であれば「> tmp-out.txt」の部分.

そもそも, ケースA にしても「シェルスクリプトから呼び出されたスクリプト」つまり tmp2.sh の中では標準出力についてなにもしていなくって, だからこそ tmp-out.txt に出力されている. これが
echo abc | sed '' > hoge
echo abc | sed 's/^/Hi-/' > foo
のようにさらにリダイレクトしてると, また違う動作になるんだけど, ね.
    • good
    • 0

その【ケースA】~【ケースD】のうち, あなたのいう


「シェルスクリプトから呼び出されたスクリプト内での複数の標準出力リダイレクトは追記される」
にあてはまるのはどれなのでしょうか? それぞれ,
「『シェルスクリプト』から呼び出された『スクリプト』内での複数の『標準出力リダイレクト』は追記される」

・シェルスクリプト
・スクリプト
・標準出力リダイレクト
はどれを指しているのでしょうか?
    • good
    • 0
この回答へのお礼

> 「シェルスクリプトから呼び出されたスクリプト内での複数の標準出力リダイレクトは追記される」
にあてはまるのはどれなのでしょうか?

ケースA, B, Cと考えています。


> ・シェルスクリプト
> ・スクリプト
> ・標準出力リダイレクト
> はどれを指しているのでしょうか?

・シェルスクリプト 先の例での tmp.sh hoge.sh bar.hsです。
・スクリプト 先の例での tmp2.sh hoge2.sh bar2.shです。
・標準出力リダイレクト 先の例での tmp-out.txt hoge-out.txt bar-out.txtです。

お礼日時:2018/08/26 16:22

> 「シェルスクリプトの中でコマンドAを呼び出し、コマンドAの中で上書き標準出力リダイレクトが複数行われるとファイルに追加される。


> シェルスクリプトの中でコマンドAとコマンドBを呼び出して、各コマンドが同じファイルに上書き標準出力をリダイレクトすると、最後の出力だけが記録される」ということですね。
> 前段が理解できないです。

もっと簡単に考えるのがいいと思うんですけどね。
「シェルスクリプトの中でコマンドAを呼び出し、コマンドAの実行結果をリダイレクトでファイルへ出力。」

それぞれ別のファイルへ書き出したいというのであれば別ですが。今回の例であればコマンド内で出力が何度行われたかは考慮する意味がありません。
あくまでそのコマンドを実行した結果得られた出力をリダイレクトしているだけですから。

そもそも ls の出力にしたって内部的に print 文を何度実行しているのか分かったものではないでしょう。
    • good
    • 0
この回答へのお礼

f02e様、ありがとうございます。
すっきりしませんが、「ls の出力にしたって内部的に print 文を何度実行しているのか分かったものではないでしょう」にヒントがあるように思いました。

お礼日時:2018/08/25 07:32

え? ごめん,


「シェルスクリプトの中でコマンドAを呼び出し、コマンドAの中で上書き標準出力リダイレクトが複数行われるとファイルに追加される。」
がどのような挙動を意味しているのかわかんない.

具体的に「こんなふうにかくとこうなる」って説明してもらえないかな. 「ファイルに追加される」の「ファイル」がなにをさすかもわかんないし.
    • good
    • 0
この回答へのお礼

Tacosan様、ありがとうございます。
以下の4ケースを確認しました。
「シェルスクリプトから呼び出されたスクリプト内での複数の標準出力リダイレクトは追記される」ということですね。

【ケースA】
(tmp.sh)
/bin/sh tmp2.sh > tmp-out.txt

(tmp2.sh)
echo abc | sed ''
echo abc | sed 's/^/Hi-/'

結果
$ /bin/sh ./tmp.sh
$ cat tmp-out.txt
abc
Hi-abc

【ケースB】
(hoge.sh)
ruby hoge2.rb > hoge-out.txt

(hoge2.rb)
puts "abc"
puts "def"

結果
$ /bin/sh ./hoge.sh
$ cat hoge-out.txt
abc
def

【ケースC】
(bar.sh)
/bin/sh bar2.sh > bar-out.txt

(bar2.sh)
for 1 2 ; do
echo xy
done

結果
$ /bin/sh ./bar.sh
$ cat bar-out.txt
xy
xy

【ケースD】
(coo.sh)
for i in 1 2 ; do
ruby coo2.rb > coo-out.txt
done

(coo2.rb)
puts "gh"

結果
$ /bin/sh ./coo.sh
$ cat coo-out.txt
gh

お礼日時:2018/08/25 08:28

echo /dev/null > hoge-out.txt


だと「/dev/null」という 1行からなる hoge-out.txt になりますね>#5. それを意図してるならいいけど, ファイルの大きさを 0 にするなら
cat /dev/null > hoge-out.txt
: > hoge-out.txt
のどちらかでしょう.

あるいは, for 全体に対してリダイレクトすることもできます (ただし意図しない結果になることもある).

あと「実行中のスクリプトの標準出力が閉じられる」という点については, foo.sh を実行中に foo.sh の標準出力がと辞されることは*自分で閉じる*のでなければ限り発生しません. 一般にあるプロセスがオープンしたファイルは
・そのプロセスで明示的にクローズする
・そのプロセスが終了する
のどちらかによってクローズされます.
    • good
    • 0

> どういう場合に、実行中のスクリプトの標準出力が閉じられるのでしょうか?



シェルスクリプトだからね、各行の実行単位で必ずクローズされる。
これは ”>” でも ”>>” でもどちらを使用しても同じこと。
ただ、”>” は実行毎にファイルの先頭から記録するのに対し、”>>” は実行毎にファイルの終端から記録していくという動作の違いがあるだけ。

捕捉にあるシェルで言うなら、1,2,3,4,5 それぞれでファイルの先頭から記録してるんだから最終的に5の実行結果しか残らない。
すべて残すなら、
ruby hoge.rb $i >> hoge-out.txt
とするか、
ruby hoge.rb $i > hoge$i-out.txt
とでもするべき。

すべて ”>>” で記録するならシェルの先頭で、
echo /dev/null > hoge-out.txt
と書いておけばシェルの実行毎にログの内容を新しく記録しなおせる。
上記 echo を実行しなければ ”>>” ですべて記録するとシェル実行毎にログが肥大化していく。
    • good
    • 0
この回答へのお礼

f02e様、詳しい解説をありがとうございます。たいへん勉強になります。
No.4回答のお礼にも書きましたが、No.1回答のお礼に書き込んだケースとの違いがわかりません。

> 次のスクリプトtmp.shでは、standard-out-from-tmp2sh.txtにtmp2.shの2つのlsコマンドの結果が書き込まれます。
> これは違う事情なのでしょうか?
>
> (tmp.sh)
> #!/bin/bash
> ScriptName=$(basename $0)
> tmp2.sh > standard-out-from-tmp2sh.txt
>
> (tmp2.sh)
> #!/bin/bash
> ls | sed ''
> ls | sed 's/^/HI/

お礼日時:2018/08/23 08:06

いや, そのスクリプトなら当然そうなりますよ.


ruby hoge.rb $i > hoge-out.txt
というコマンドを, i を 1 から 5 まで変えながら実行するんだよ. もっと端的にいえば, i を除けば
ruby hoge.rb 1 > hoge-out.txt
ruby hoge.rb 2 > hoge-out.txt
ruby hoge.rb 3 > hoge-out.txt
ruby hoge.rb 4 > hoge-out.txt
ruby hoge.rb 5 > hoge-out.txt
と同じことをやってるんだもん.
    • good
    • 0
この回答へのお礼

Tacosan様、ありがとうございます。
No.1回答のお礼に書き込んだケースとの違いがわかりません。

> 次のスクリプトtmp.shでは、standard-out-from-tmp2sh.txtにtmp2.shの2つのlsコマンドの結果が書き込まれます。
> これは違う事情なのでしょうか?
>
> (tmp.sh)
> #!/bin/bash
> ScriptName=$(basename $0)
> tmp2.sh > standard-out-from-tmp2sh.txt
>
> (tmp2.sh)
> #!/bin/bash
> ls | sed ''
> ls | sed 's/^/HI/'

お礼日時:2018/08/23 08:01

たとえば


hoge.sh > foo.txt
という形であれば, hoge.sh 中の標準出力は foo.txt に書き出されます. この処理は「ファイルディスクリプタ 1 が foo.txt を指す」ことで実現し, hoge.sh が動いている間 foo.txt はオープンしっぱなしになります (hoge.sh が終了するときにクローズされる).

ただし, あたりまえですが
・hoge.sh の中で foo.txt をいじる
・他のプロセスで foo.txt をオープンする
など, 競合する状況が発生していたらどうなるかはわかりません.

「ファイルサイズが途中で減る」ときに, そのファイルの中身がどうなっているかがわかればもうちょっとなんか書ける... かなぁ.
    • good
    • 0
この回答へのお礼

Tacosan様、ありがとうございます。
やはり、「hoge.sh が動いている間 foo.txt はオープンしっぱなしになります」ですね。
動かしているスクリプトのどのタイミングでファイルサイズが減っているのかわからないので、「そのファイルの中身がどうなっているか」をお示しできません。
しばらく自分で調べます。
(リダイレクトを ">" から ">>" に変更して終わりそう)

お礼日時:2018/08/19 14:58

ごめんなさい。


Linuxは方言がきついので自分にはそのスクリプトに使われているコマンド(のオプション)を理解できない。
(コマンドリファレンスやらセルフドキュメントがないと自分は能無しです)

ただ、繰り返し ”tmp.sh” を呼び出すなら、都度 ”standard-out-from-tmp2sh.txt” ファイルが上書きされるということは分かります。
    • good
    • 0
この回答へのお礼

銀鱗様、ありがとうございます。
繰り返しですが、tmp.sh の中でtmp2 > file.txt を実行し、
tmp2の中で標準出力を2回出力したら、file.txtにすべて記録されます。
2つ目の出力だけでなく、すべて記録されるのはなぜでしょうか。

外部コマンドの方がよいかと思い、tmp2の中で "ls | sed" "ls | sed 's/^/HI/'"としましたが、これは
echo abc
echo def
としても同じことと思います。

お礼日時:2018/08/18 22:40

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