Lから始まるLinux

2章47話
グルーピング
2章48話 同一ファイルへの入出力
もくじ
2章49話
ハッシュ(基本)
同一ファイルへの入出力
若木 みどり
お兄ちゃん! 入力リダイレクト < 出力リダイレクト > 同じファイルで 同時に使おうとすると おかしなことになるんだ!
若木 みどり
まずは 以下のコマンドで ファイルを作ったよ
cat > list << EOF
1
2
3
4
5
EOF
若木 みどり
この内容をシャッフルしようとして以下を実行したんだ
shuf < list > list
若木 みどり(驚き)
そうしたらlist ファイルの中身が空になっちゃったんだよ!
若木 しげる
これは Linux を操作していると時々やってしまうことだね今回は入力と出力リダイレクトを同一ファイルへ同時に行うと出力先が空ファイルになってしまう原因とそれを解消する方法を学ぼう!
原因
若木 しげる
ファイルの中身が空になった原因は出力リダイレクト > だよ
若木 みどり
ファイルの中身を上書きする出力リダイレクトだよね!
若木 しげる
出力リダイレクト > が使われると最初にファイルの中身が空にされるんだリダイレクトの順序に関係なく出力リダイレクト先のファイルが真っ先に空にされるよ
若木 みどり(驚き)
なるほど!入力リダイレクト <list ファイルを読み込んだけどこの時点ですでに内容が空だったんだね!
若木 しげる
そういうことだね空の内容をシャッフルしてもその結果は空のままだよねその空の内容を書き出したので最終的に list の中身は空というわけなんだ
解決法
若木 みどり
この問題はどうやって解消したらいいんだろうね?
若木 しげる
入力と出力リダイレクトで同じファイルを指定しなければこの問題を解消できるよいくつか方法があるので見てみよう!
一時ファイルを作る
若木 しげる
コマンドを分割して入力と出力リダイレクトを同時に使わないようにすれば期待どおりに動作するよ
若木 しげる
以下のようにしてみよう
shuf < list > list.output
mv list{.output,}
若木 みどり
入力リダイレクトの対象は list出力リダイレクトの対象は list.output だね入出力リダイレクトでそれぞれ別のファイルを指定することで問題を回避しているんだ!
若木 みどり
その後 mvlist.outputlist移動して上書きしているね操作を二段階に分けてやりたいことを実現しているんだ!
若木 しげる
Linux に慣れていないならこの方法がわかりやすくておすすめだよ
tee を使う
若木 しげる
tee標準出力とファイルの二つへ出力を書き込むよT字路の分岐みたいだからこの名前なんだ
tee [オプション]... [ファイル]...
若木 みどり
画面にもファイルにも出力…欲張りさんなコマンドだね!
若木 しげる
動作を確かめるために以下のコマンドを実行してみよう!
tee ~/tee-outout <<< hello
若木 みどり
まずは画面にhello出るはずだよね?
hello
若木 みどり
よし!画面出力は確認できたね!
若木 しげる
もう一つの出力先も見てみよう引数で指定した~/tee-outout ファイルだね
cat ~/tee-outout
若木 みどり
こっちにも同じ内容が書かれていると思うよ!
hello
若木 しげる
この tee出力リダイレクトの代わりに使うことで問題を解決できるよ
shuf < list | tee list
若木 みどり
なるほど!出力リダイレクトを使わず代わりに tee を使うことで出力リダイレクト先が空になるという挙動を回避しているんだね!
若木 みどり
期待どおりに動いたけどファイル内容が標準出力にも表示されちゃうねちょっと気になるかも
若木 しげる
確かにそう感じるよねでも画面に内容が表示されても害はないし簡単に対処できる方法なんだ
若木 しげる(笑顔)
どうしても気になるなら出力を /dev/nullリダイレクトしよう!
グルーピングを使う
若木 しげる
最後はグルーピングを使う方法だよ
{
  rm list
  cat | shuf > list
} < list
若木 しげる
先に言っておくけど複雑で難しいので無理に使う必要はないよ
若木 みどり(驚き)
な…何が何だかわからないよ!
若木 みどり
とりあえずわかりそうな所から見ていくねグルーピング { ... } の中でrm を使っているけどどうしてファイルを削除しているのかな?
若木 しげる
ファイルを削除することでその後の出力リダイレクトshuf > list「新しいファイル list に書き出す」という動作になるんだ入力リダイレクト < listファイル名は同じだけど入出力リダイレクトで別ファイルを指定しているという動作をさせるために使っているよ
若木 みどり
cat を引数なしで使っているみたいだけどこれはどういう動きをするのかな?
若木 しげる
cat を引数なしで実行すると読み込む内容を標準入力から受け付けるよグルーピング { ... } の外の入力リダイレクト < listから入力を読み込みパイプで次のコマンドにその内容を渡しているんだ
若木 しげる
かなりトリッキーな方法なので理屈はわからなくて大丈夫だよこの方法を使うつもりなら書き方で覚えてしまおう!
まとめ
若木 みどり
今回は入力と出力リダイレクトを同時に使うときに同じファイルを指定すると期待通りに動かないことを学んだよこの問題を避けるにはいくつか方法があるんだ!
若木 しげる
「入力」「出力リダイレクト」同じファイルを指定していればこの問題は発生するよ
若木 しげる
例えば以下の例でも同じことが起こるんだ
cat list > list
若木 みどり
結構簡単に起こってしまうんだね…知らないうちにやってしまいそう
若木 しげる
コマンド実行で目的のファイルが空ファイルになっていたら今回の挙動を疑ってみよう!