シェル芸家元より、GWの宿題が出題されました。
ゴールデンウイークシェル芸問題 | 上田ブログさて、これの関係で、入力ファイルに直接リダイレクトして書き込みができるかどうか、問題を解く前に調べておこうと思いちょっと実験してみました。
日頃気になってたことでもありますし・・・環境はいつもの通り、Windows7上のVMWare Player上のreeBSD 10.1 amd64 のJail環境です。

準備

1
2
3
4
5
6
7
8
% jot 5 > a
% cat a
1
2
3
4
5

とりあえず色々やってみるテスト

①-1.自分にcatして書き込み

1
2
3
% cat a > a
% cat a

消えました

①-2.自分にcatして追加書き込み

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
% cat a >> a
^C
% wc -l a
  261940 a
% head -12 a
1
2
3
4
5
1
2
3
4
5
1
2

1~5を何度も繰り返している模様


②-1. 1行ずつreadして書き込み

1
2
3
4
% cat a | while read L; do echo 2:$L > a;done
% cat a
2:5

最後の1行で上書きされる

②-2. 1行ずつreadして追加書き込み

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
% cat a | while read L; do echo 2:$L >> a;done
% cat a
1
2
3
4
5
2:1
2:2
2:3
2:4
2:5

元の内容の後ろに付く感じ

③-1.xargsの先で書き込み

1
2
3
% cat a | xargs -I@ echo @ > a
% cat a

これも消える

③-2.xargsの先で追加書き込み

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
% cat a | xargs -I@ echo 2:@ >> a
% cat a
1
2
3
4
5
2:1
2:2
2:3
2:4
2:5

元の内容の後ろに付く感じ

④-1.xargsからshを呼び出して書き込み

1
2
3
4
% cat a | xargs -I@ echo "echo 2:@ > a" | sh
% cat a
2:5

最後の1行で上書きされる

④-2.xargsからshを呼び出して追加書き込み

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
% cat a | xargs -I@ echo "echo 2:@ >> a" | sh
% cat a
1
2
3
4
5
2:1
2:2
2:3
2:4
2:5

元の内容の後ろに付く感じ

⑤-1.sedして書き込み

1
2
3
% sed 's/^/2:/g' a > a
% cat a

これも消える

⑤-2.sedして追加書き込み

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
% sed 's/^/2:/g' a >> a
% cat a
1
2
3
4
5
2:1
2:2
2:3
2:4
2:5

これは後ろに付く

傾向が見えてきました。

「②-1.」の結果を応用してみます。

1
2
3
4
% cat a | xargs | while read L ;do echo $L; done > a
% cat a
1 2 3 4 5

なるほどなるほど・・・つまりこうすればよいと。

1
2
3
4
5
6
7
8
% cat a | xargs | while read L ;do echo $L; done | xargs -n 1 | xargs -I@ echo 2:@ > a
% cat a
2:1
2:2
2:3
2:4
2:5

うむ。おそらく、ファイルをすべて読み込んだ後だと、一旦ファイルが閉じられて、書き込みができるようになっているって感じでしょうか。

さらに応用して

ファイルを閉じてから書き込めば良い、というふうに考えると、そういうコマンドを使えばいいわけで。

⑥-1.awkで直接書き込み

1
2
3
% cat a | awk '{print "2:"$0}' > a
% cat a

これは消えるけれど

⑥-2.awkの結果をcatして書き込み

1
2
3
4
5
6
7
8
% cat a | awk '{print "2:"$0}' | cat > a
% cat a
2:1
2:2
2:3
2:4
2:5

っていうことなんですよね。
確か、awkをパイプで繋いだら、awkの処理が全て終わってから次の処理へ行くような動きをしていたような感じなので……

さらにさらに応用

上記のawkの性質を「⑤-1.sedして書き込み」に応用して利用してみます。

1
2
3
4
5
6
7
8
% sed 's/^/2:/g' a | awk '{print}' | cat > a
% cat a
2:1
2:2
2:3
2:4
2:5

はい、できました。
※注意:これはあくまで限定された環境で小さなデータを使った雑な実験です。大きなデータや上記以外のデータを使ったらどうなるかは確かめてませんのであしからず!

2015/5/3 追記

シェルによって挙動が異なる模様。Cシェル系はだいたい上記の通りになって、Bシェル系はそうならないみたい?