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


110

temp.txtというファイルがありsort、bash のコマンドで並べ替えます。

並べ替えた結果で元のファイルを置き換えたい。

たとえば、これは機能しません(空のファイルが表示されます)。

sortx temp.txt > temp.txt

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


編集:-oオプションは非常にクールですsortsort私の質問では例として使用しました。他のコマンドでも同じ問題が発生します。

uniq temp.txt > temp.txt.

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


回答:


171
sort temp.txt -o temp.txt

3
これが答えです。この問題に対する一般的な解決策があるかどうか、私は実際に考えていました。たとえば、ファイル内のすべてのUNIQ行を「インプレース」で検索したい場合、-o
jmを

これは一般的ではありませんが、GNU sortで-uを使用して一意の行を見つけることができます
James

誰かが問題を解決して例えば許可しましたsort --inplace *.txtか?それは
すごい

@seheこれを試してください:find . -name \*.txt -exec sort {} -o {} \;
キース・ゴーハン

29

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

sort temp.txt -o temp.txt

具体的には、GNUsortドキュメントには次のように書かれています。

通常、sortはoutput-fileを開く前にすべての入力を読み取ります。そのため、sort -o F Fやなどのコマンドを使用して、ファイルを安全に並べ替えることができますcat F | sort -o F。しかし、sort--merge-m)コマンドのようなので、すべての入力を読み込む前に、出力ファイルを開くことができますcat F | sort -m -o F - Gソートが書き始める可能性があるとして、安全ではないFの前にcatそれを読んで行われます。

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でも、お好みのスクリプト言語で同じことができます。ただし、ファイル全体を格納するのに十分なメモリが必要になることに注意してください。これは、大きなファイルを扱う場合にはお勧めできません。


19

これはより一般的なアプローチで、uniq、sort、whatnotなどで動作します。

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

14
spongemoreutilsからの別の一般的なアプローチ:cat file |frobnicate |sponge file
東武

3
@東武:別の回答として提出してみませんか?
Flimm 2011

1
これは、必ずしもファイルのアクセス許可を保持するわけではないことに注意してください。umaskは、新しい権限がどうなるかを決定します。
2014年

1
トリッキーなもの。それがどのように正確に機能するかを説明できますか?
patryk.beza

2
@ patryk.beza:順番に:入力FDが元のファイルから開かれます。元のディレクトリエントリは削除されます。リダイレクトが処理され、古いファイルと同じ名前の新しい空のファイルが作成されます。その後、コマンドが実行されます。
Charles Duffy

10

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

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なくなった。


1
Spongeは、入力ファイルの置き換えに使用されることを認識しており、競合状態を回避するために最初に一時ファイルを作成します。これが機能するためには、スポンジがパイプラインの最後の要素である必要があり、(たとえば、シェルレベルの出力リダイレクトとは対照的に)出力ファイル自体を作成できる必要があります。ところで、「失敗」の場合の簡単なソースコード修正は、パイプが失敗した場合に一時ファイルの名前を変更しないことです(スポンジにそのオプションがない理由がわからない)。
ブレントブラッドバーン、2014年

set -o pipefailスクリプトの先頭に追加した場合、エラーが発生mistyped_command my-important-fileするとsponge、が実行される前にスクリプトがすぐに終了し、重要なファイルが保持されると思います。
Elouan Keryell-Even

6

1行です。

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

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


6
うーん。temp.txt.sortを一時ファイルと呼びます。
JesperE 2008

5
このコードは危険です。ジョブが完了せずに何らかの理由でソートが失敗した場合、元のコードが上書きされるためです。
Steve Jessop

1
考えられる原因であるディスク領域の不足、またはシグナル(ユーザーがCTRL-Cを押した)。
スティーブジェソップ

5
このようなものを使用したい場合は、代わりに&&(論理and)を使用してください。これを使用すると、コマンドが失敗した場合に次のコマンドが実行されないことが確認されます。例:cp backup.tar /root/backup.tar && rm backup.tarコピーする権限がない場合、ファイルは削除されないため安全です
daniels

1
おかげであなたの提案を考慮に入れるために私の答えを変更しました、ありがとう
davr

4

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

BASH 履歴拡張の使用:

$ sort file -o !#^

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

独自の並べ替え:

$ sort -u -o file !#$

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


3

多くの人が-oオプションについて言及しています。これはマニュアルページの部分です。

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

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

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

コマンド(この場合)がファイルを読み取る前に、ファイルが切り捨てられる可能性があると思います。>uniq
マーティン

3

spongeより一般的なの代替sed

sed -ni r<(command file) file

これは、任意のコマンドのために働く(sortuniqtac、...)と非常によく知られている使用sed-iオプション(インプレース編集ファイル)。

警告:command fileインプレースでのファイルの編集は本来安全ではないため、最初に試してください。


説明

最初に、sed(元の)行(-nオプション)を印刷しないように指示します。また、sed's rコマンドbash' Process Substitution 'の助けを借りて、生成されたコンテンツは所定の場所に<(command file)保存さた出力になります。


物事をさらに簡単にする

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

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


1

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

sort inputfile | uniq | sort -o inputfile


0

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

編集:私の恥。sort temp.txt -o temp.txt素晴らしい作品。


Qも「インプレース」として読んだのですが、2回目の読み取りで、彼は本当にそれを求めていなかったと思いました
epatel

0

別の解決策:

uniq file 1<> file

ただし、<>トリックはこの場合にのみ機能することに注意してください。これは、uniq入力ラインを出力ラインにコピーするだけであり、途中でドロップするという点で特別なためです。他のコマンド(例えば場合sed)の入力を変更することになる使用された(例えば、すべて変わってしまうaにはaa、それは上書きすることができ、) file(入力が十分に大きいことをA以上のものを提供し、任意の感覚と無限にさえループを作らない方法で、単一の読み取りバッファ)。
David
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.