お兄ちゃん!
入力リダイレクト
< と
出力リダイレクト > を
同じファイルで
同時に使おうとすると
おかしなことになるんだ!
まずは
以下のコマンドで
ファイルを作ったよ
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 だね入出力リダイレクトでそれぞれ別のファイルを指定することで問題を回避しているんだ!その後
mv でlist.output を list に移動して上書きしているね操作を二段階に分けてやりたいことを実現しているんだ!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
結構簡単に起こってしまうんだね…知らないうちにやってしまいそう
コマンド実行で目的のファイルが空ファイルになっていたら今回の挙動を疑ってみよう!