web-dev-qa-db-ja.com

bashを使用してファイル(引数)を「インプレース」編集するコマンドを実行するにはどうすればよいですか?

Bashのsortコマンドでソートしたいtemp.txtファイルがあります。

ソートされた結果で元のファイルを置き換えたいです。

これは例えば機能しません(空のファイルを取得します):

sortx temp.txt > temp.txt

これは、一時ファイルへのコピーに頼らずに1行で実行できますか?


編集:-oオプションは、sortに対して非常に便利です。例としてsortを質問に使用しました。私は他のコマンドで同じ問題に遭遇します:

uniq temp.txt > temp.txt.

より良い一般的な解決策はありますか?

108
jm.
sort temp.txt -o temp.txt
171
daniels

sortは、出力を開始する前にすべての入力を確認する必要があります。このため、sortプログラムは、インプレースでファイルを変更するオプションを簡単に提供できます。

sort temp.txt -o temp.txt

具体的には、 GNU sort のドキュメント:=

通常、sortはoutput-fileを開く前にすべての入力を読み取るため、sort -o F Fcat F | sort -o Fなどのコマンドを使用して、ファイルを所定の場所に安全にソートできます。ただし、sort with --merge-m)はすべての入力を読み取る前に出力ファイルを開くことができるため、cat F | sort -m -o F - Gなどのコマンドは安全ではありません。ソートはFcatが読み込まれる前。

BSDのドキュメントsortは次のように述べています:

[the] output-fileが入力ファイルの1つである場合、sortは、出力を[the] output-fileにソートして書き込む前に、それを一時ファイルにコピーします。

uniqなどのコマンドは、入力の読み取りを完了する前に出力の書き込みを開始できます。これらのコマンドは通常、インプレース編集をサポートしていません(この機能をサポートすることは困難です)。

通常、一時ファイルを使用してこの問題を回避します。中間ファイルを絶対に避けたい場合は、バッファを使用して完全な結果を保存してから書き出すことができます。たとえば、Perlの場合:

uniq temp.txt | Perl -e 'undef $/; $_ = <>; open(OUT,">temp.txt"); print OUT;'

ここでは、Perl部分は、変数$_uniqから完全な出力を読み取り、このデータで元のファイルを上書きします。お好みのスクリプト言語でも、おそらくBashでも同じことができます。ただし、ファイル全体を保存するのに十分なメモリが必要になることに注意してください。大きなファイルを操作する場合はお勧めできません。

29
Bruno De Fraine

以下に、より一般的なアプローチを示します。uniq、sort、その他の方法で動作します。

{ rm file && uniq > file; } < file
19
wor

スポンジに関する東武のコメント それ自体が答えであることを保証します。

moreutils ホームページから引用するには:

おそらく、これまでのmoreutilsの最も汎用的なツールはsponge(1)で、次のようなことができます。

% sed "s/root/toor/" /etc/passwd | grep -v joey | sponge /etc/passwd

ただし、spongeには同じ問題があります Steve Jessopがここでコメントしていますspongeより前のパイプラインのコマンドのいずれかが失敗すると、元のファイルが書き込まれます以上。

$ mistyped_command my-important-file | sponge my-important-file
mistyped-command: command not found

ええとああ、 my-important-file なくなっている。

9
Sean

ここで、1行:

sort temp.txt > temp.txt.sort && mv temp.txt.sort temp.txt

技術的には、一時ファイルへのコピーはなく、「mv」コマンドは即座に実行されるはずです。

6
davr

私は好き sort file -o file答えますが、同じファイル名を2回入力したくないです。

BASHの使用 履歴展開

$ sort file -o !#^

を押すと、現在の行の最初の引数を取得します enter

その場でのユニークなソート:

$ sort -u -o file !#$

現在の行の最後の引数を取得します。

4
johnnyB

spongeの代わりに、より一般的なsedを使用:

_sed -ni r<(command file) file
_

任意のコマンド(sortuniqtac、...)で動作し、よく知られているsedの-​​ _-i_ option (インプレースでファイルを編集)。

警告:最初に_command file_を試してください。インプレースでファイルを編集することは本質的に安全ではありません。


説明

まず、(元の)行を印刷しないようにsedに指示しています( _-n_オプション )、およびsedの助けを借りて- r command and bash 's Process Substitution<(command file)によって生成されたコンテンツは保存された出力になりますin place


物事をさらに簡単にする

このソリューションを関数にラップできます。

_ip_cmd() { # in place command
    CMD=${1:?You must specify a command}
    FILE=${2:?You must specify a file}
    sed -ni r<("$CMD" "$FILE") "$FILE"
}
_

_$ cat file
d
b
c
b
a

$ ip_cmd sort file
$ cat file
a
b
b
c
d

$ ip_cmd uniq file
$ cat file
a
b
c
d

$ ip_cmd tac file
$ cat file
d
c
b
a

$ ip_cmd
bash: 1: You must specify a command
$ ip_cmd uniq
bash: 2: You must specify a file
_
3
whoan

多くの人が-oオプションに言及しています。これがmanページの部分です。

Manページから:

   -o output-file
          Write output to output-file instead of to the  standard  output.
          If  output-file  is  one of the input files, sort copies it to a
          temporary file before sorting and writing the output to  output-
          file.
3
epatel

これは非常にメモリに制約がありますが、awkを使用して中間データをメモリに保存してから書き戻すことができます。

uniq temp.txt | awk '{line[i++] = $0}END{for(j=0;j<i;j++){print line[j]}}' > temp.txt
3
JayG

非対話型エディタ ex を参照してください。

1
slim

引数--output=または-oを使用します

FreeBSDで試しました:

sort temp.txt -otemp.txt
1
sammyo

uniq機能を追加するには、次の欠点があります。

sort inputfile | uniq | sort -o inputfile
1
jasper

sortプログラムの使用を主張する場合は、中間ファイルを使用する必要があります-sortにはメモリ内でソートするオプションはないと思います。ソートのstdinのバッファーサイズがファイル全体に適合するのに十分であることを保証できない限り、stdin/stdoutを使用したその他のトリックは失敗します。

編集:恥を知れ。 sort temp.txt -o temp.txt優れた動作。

0
JesperE

別の解決策:

uniq file 1<> file
0
Antonio Lebrón