为什么 sed 根据输出文件的不同而有不同的行为?

为什么 sed 根据输出文件的不同而有不同的行为?

如果我运行:

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

在一个大文件(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

答案1

弗施密特的回答使用 sed 时最好;然而,从更一般的意义上来说,这种反模式:

cat infile | filter > infile

可能会给您带来很多问题。例如,如果我有一个名为infile如下的文件:

Hello
World

并运行此命令:

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

我明白了

hello
world

但如果我运行cat infile | tr "[:upper:]" "[:lower:]" > infile我会得到一个空文件。为什么?

好吧,当您使用输出重定向运算符时,>您是在说“将我的标准输出放入此文件中,如果该文件存在则覆盖它。”现在您可能认为这应该有效,因为您的过滤器将返回原始文件的所有行。然而,最终经常发生的情况是 shell 会在读取任何行之前破坏您的文件。然后,您的过滤器命令将从空文件中读取行,找不到任何行,因此不返回任何行。在某些地方,您可能会“幸运”地在文件被破坏之前读取一些行,但最好完全避免这种模式。

要解决这个特定问题,您有几种选择。一种是简单地执行以下操作:

cat infile | filter > tmpfile; mv tmpfile infile

如果您需要确保您的临时文件不会破坏其他文件或发生其他令人讨厌的事情,您应该查看mktemp. (参见man mktempinfo coreutils mktemp

另一种选择是使用spongefrom更多实用程序

此外,其中许多例子都是猫的无用用途

答案2

你为什么不直接写

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

-i 表示“到位”

答案3

就地编辑文件的另一种(便携式)方法是使用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

答案4

您可以在 Ex 模式下使用 Vim:

ex -sc '%s/OLD/NEW/g|x' messages.txt
  1. %选择所有行

  2. s代替

  3. g全局替换

  4. x保存并关闭

相关内容