bashでファイルを処理して結果をそのファイルに上書きする

bashでファイルを利用して処理を行い、その結果を処理に使ったファイルに上書き・追記するといった場合、普通にやると対象のファイルが空になってしまう。

[root@BS-PUB-CENT7-01 ~]# cat -n test.txt
1  a01 a02 a03 a04 a05 a06 a07 a08 a09 a10
2  b01 b02 b03 b04 b05 b06 b07 b08 b09 b10
3  c01 c02 c03 c04 c05 c06 c07 c08 c09 c10
4  d01 d02 d03 d04 d05 d06 d07 d08 d09 d10
5  e01 e02 e03 e04 e05 e06 e07 e08 e09 e10
6  f01 f02 f03 f04 f05 f06 f07 f08 f09 f10
7  g01 g02 g03 g04 g05 g06 g07 g08 g09 g10
8  h01 h02 h03 h04 h05 h06 h07 h08 h09 h10
9  i01 i02 i03 i04 i05 i06 i07 i08 i09 i10
10  j01 j02 j03 j04 j05 j06 j07 j08 j09 j10
[root@BS-PUB-CENT7-01 ~]# cat -n test.txt | head -1 > ./test.txt
[root@BS-PUB-CENT7-01 ~]# cat ./test.txt
[root@BS-PUB-CENT7-01 ~]#

 

これは、上書きだった場合にはリダイレクト実行時に空ファイルが自動的に作られ、そこに実行結果を記述するといった動きをするかららしい。
そのためか、追記は普通にできたりする(grepなど、一部のコマンドはチェック処理が走るためダメ)。

[root@BS-PUB-CENT7-01 ~]# cat -n test.txt
1  a01 a02 a03 a04 a05 a06 a07 a08 a09 a10
2  b01 b02 b03 b04 b05 b06 b07 b08 b09 b10
3  c01 c02 c03 c04 c05 c06 c07 c08 c09 c10
4  d01 d02 d03 d04 d05 d06 d07 d08 d09 d10
5  e01 e02 e03 e04 e05 e06 e07 e08 e09 e10
6  f01 f02 f03 f04 f05 f06 f07 f08 f09 f10
7  g01 g02 g03 g04 g05 g06 g07 g08 g09 g10
8  h01 h02 h03 h04 h05 h06 h07 h08 h09 h10
9  i01 i02 i03 i04 i05 i06 i07 i08 i09 i10
10  j01 j02 j03 j04 j05 j06 j07 j08 j09 j10
[root@BS-PUB-CENT7-01 ~]# cat -n test.txt | head -1 >> ./test.txt
[root@BS-PUB-CENT7-01 ~]# cat test.txt
a01 a02 a03 a04 a05 a06 a07 a08 a09 a10
b01 b02 b03 b04 b05 b06 b07 b08 b09 b10
c01 c02 c03 c04 c05 c06 c07 c08 c09 c10
d01 d02 d03 d04 d05 d06 d07 d08 d09 d10
e01 e02 e03 e04 e05 e06 e07 e08 e09 e10
f01 f02 f03 f04 f05 f06 f07 f08 f09 f10
g01 g02 g03 g04 g05 g06 g07 g08 g09 g10
h01 h02 h03 h04 h05 h06 h07 h08 h09 h10
i01 i02 i03 i04 i05 i06 i07 i08 i09 i10
j01 j02 j03 j04 j05 j06 j07 j08 j09 j10
     1  a01 a02 a03 a04 a05 a06 a07 a08 a09 a10

 

では、処理結果をどのように上書きすればよいか。一時ファイルなんて作りたくはない…。しかし、テストしたがファイルディスクリプタを使ってもうまくはいかないので、どうすればいいか。
色々と調べてみたところ、以下の2通りのやり方がありそうだ。

Sponsored Links

1.プロセス置換を利用する

プロセス置換を利用して、以下のようにコマンドを実行することで上書き処理が行えた。
なお、この際sleepコマンドが無いと、リダイレクトが行われる処理のタイミングによってはそのままからファイルになってしまうことがあるので注意。

コマンド File.path > >( sleep 1 && cat > File.path)
[root@BS-PUB-CENT7-01 ~]# cat test.txt
a01 a02 a03 a04 a05 a06 a07 a08 a09 a10
b01 b02 b03 b04 b05 b06 b07 b08 b09 b10
c01 c02 c03 c04 c05 c06 c07 c08 c09 c10
d01 d02 d03 d04 d05 d06 d07 d08 d09 d10
e01 e02 e03 e04 e05 e06 e07 e08 e09 e10
f01 f02 f03 f04 f05 f06 f07 f08 f09 f10
g01 g02 g03 g04 g05 g06 g07 g08 g09 g10
h01 h02 h03 h04 h05 h06 h07 h08 h09 h10
i01 i02 i03 i04 i05 i06 i07 i08 i09 i10
j01 j02 j03 j04 j05 j06 j07 j08 j09 j10
[root@BS-PUB-CENT7-01 ~]# head -1 test.txt
a01 a02 a03 a04 a05 a06 a07 a08 a09 a10
[root@BS-PUB-CENT7-01 ~]# head -1 test.txt > >(sleep 1 && cat > test.txt)
[root@BS-PUB-CENT7-01 ~]# cat test.txt
a01 a02 a03 a04 a05 a06 a07 a08 a09 a10
[root@BS-PUB-CENT7-01 ~]#

2.moreutilsのspongeコマンドを用いる

上記のプロセス置換を利用する方法のほか、moreutilsにあるspongeコマンドを用いることで、そのままファイルを上書きできる。

コマンド File.path | sponge File.path
[root@BS-PUB-CENT7-01 ~]# cat test.txt
a01 a02 a03 a04 a05 a06 a07 a08 a09 a10
b01 b02 b03 b04 b05 b06 b07 b08 b09 b10
c01 c02 c03 c04 c05 c06 c07 c08 c09 c10
d01 d02 d03 d04 d05 d06 d07 d08 d09 d10
e01 e02 e03 e04 e05 e06 e07 e08 e09 e10
f01 f02 f03 f04 f05 f06 f07 f08 f09 f10
g01 g02 g03 g04 g05 g06 g07 g08 g09 g10
h01 h02 h03 h04 h05 h06 h07 h08 h09 h10
i01 i02 i03 i04 i05 i06 i07 i08 i09 i10
j01 j02 j03 j04 j05 j06 j07 j08 j09 j10
[root@BS-PUB-CENT7-01 ~]# head -2 test.txt
a01 a02 a03 a04 a05 a06 a07 a08 a09 a10
b01 b02 b03 b04 b05 b06 b07 b08 b09 b10
[root@BS-PUB-CENT7-01 ~]# head -2 test.txt | sponge test.txt
[root@BS-PUB-CENT7-01 ~]# cat test.txt
a01 a02 a03 a04 a05 a06 a07 a08 a09 a10
b01 b02 b03 b04 b05 b06 b07 b08 b09 b10

 

sedなど、上書きオプションの無いコマンドの結果を上書きしたい場合、このような方法で(半ば無理やり感はあるけど)上書きは可能なようだ。


Written by blacknon

インフラ系のSE。一時期はプログラマ。 仮想化とオープンソースに興味あり。一日中寝てたい今日このごろ。 スペインとかで働きたいなぁ…(シエスタがあるので)

Leave a Comment

メールアドレスが公開されることはありません。

*