web-dev-qa-db-ja.com

sedが出力ファイルによって動作が異なるのはなぜですか?

私が実行した場合:

cat messages.txt | sed -e 's/a/a/g' > messages.txt

1つの大きなファイル(2500行以上)で、結果のファイルはcygwinのコマンドの後に約900行しかなく、gentooの行はありません。しかし、私が実行した場合

cat messages.txt | sed -e 's/a/a/g' > other_messages.txt

すべての行を適切に保持します。

私の質問はなぜですか、それ以外の方法はありますか

cat messages.txt | sed -e 's/a/a/g' > other_messages.txt
rm messages.txt
mv other_messages.txt messages.txt
5
ashleysmithgpu

書いてみませんか

sed -i -e 's/a/a/g' messages.txt

-iは「インプレース」を意味します

10
fschmitt

fschmittの答え sedを使用する場合に最適です。ただし、より一般的な意味では、このアンチパターンは次のとおりです。

cat infile | filter > infile

かなりの数の問題を引き起こす可能性があります。たとえば、次のようなinfileというファイルがある場合:

Hello
World

次のコマンドを実行します。

cat infile | tr "[:upper:]" "[:lower:]"

私は得る

hello
world

しかし、私が実行した場合cat infile | tr "[:upper:]" "[:lower:]" > infile空のファイルを取得します。どうして?

さて、あなたが出力リダイレクト演算子を使用するとき>「標準出力をこのファイルに入れ、そのファイルが存在する場合は上書きしてください」と言っています。フィルタは元のファイルのすべての行を返すので、これでうまくいくと思うかもしれません。ただし、多くの場合、最終的に発生するのは、行が読み取られる前にシェルがファイルを上書きすることです。次に、filterコマンドは、空のファイルから行を読み取り、何も検出しないため、何も返しません。いくつかの場所では、ファイルが壊れる前にいくつかの行を読み取らせるのに十分な「幸運」になるかもしれませんが、このパターンを完全に回避するのが最善です。

この特定の問題を回避するには、いくつかのオプションがあります。 1つは、次のようなことを行うだけです。

cat infile | filter > tmpfile; mv tmpfile infile

一時ファイルが他のファイルを壊したり、他の厄介なことが起こったりしないことを確認する必要がある場合は、mktempを調べる必要があります。 (見る man mktempおよびinfo coreutils mktemp

別のオプションは、 moreutils からspongeを使用することです。

また、これらの例の多くは catの無用な使用 の例です。

11
Steven D

ファイルをインプレースで編集するさらに別の(ポータブルな)方法は、edを使用することです。

# cf. http://wiki.bash-hackers.org/howto/edit-ed
cat <<-'EOF' | ed -s messages.txt
H
,g/a/s//b/g
wq
EOF


# ... or read the file contents into a variable, modify it and write it back to file
file_contents="$(cat messages.txt)"
printf '%s' "$file_contents" | sed -e 's/a/b/g' > messages.txt


# ... and, if you want to play around with a file descriptor hack, ...
# (As long as there's a fd associated with a file, the file can be accessed via the fd.) 

exec 3<messages.txt  # open file on fd 3 for reading
rm -f messages.txt
sed -e 's/a/b/g' <&3 > messages.txt
2
xenod

ExモードでVimを使用できます:

ex -sc '%s/OLD/NEW/g|x' messages.txt
  1. %すべての行を選択

  2. s代替

  3. gグローバル置換

  4. x保存して閉じる

0
Steven Penny