在文件中查找和替换,覆盖文件不起作用,它清空了文件


604

我想通过命令行在HTML文件上运行查找和替换。

我的命令如下所示:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html > index.html

当我运行此文件并随后查看文件时,它为空。它删除了我文件的内容。

当我再次还原文件后运行此命令时:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

stdout是该文件的内容,查找和替换已经被执行。

为什么会这样呢?


13
Perl替代方案:perl -pi -w -e 's/STRING_TO_REPLACE/REPLACE_WITH/g;' index.html
Gjorgji Tashkovski

很多相关的sed命令来查找字符串并替换整行:stackoverflow.com/questions/11245144/…–
cregox

Answers:


917

外壳程序> index.html在命令行中看到 该命令时,它将打开文件index.html进行写入,从而清除其所有先前内容。

要解决此问题,您需要传递-i选项以sed进行内联更改,并在对原文件进行更改之前创建原始文件的备份:

sed -i.bak s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

如果没有.bak,则该命令在某些平台(例如Mac OSX)上将失败。


20
truncates the file而不是说opens the file清楚。
Mikel

12
至少在我的Mac上,第一个建议不起作用...如果要在文件上进行就地替换,则必须指定扩展名。您至少可以传递零长度的扩展名:sed -i''s / STRING_TO_REPLACE / STRING_TO_REPLACE_IT / g index.html
Tom Lianza 2011年

5
对于变量sed -i.bak's /'$ search'/'$ replace'/ g'index.html
Fatima Zohra 2013年

33
在osx上,使用空字符串''作为-i的参数,例如:sed -i '' 's/blah/xx/g'
Pierre Houston

4
但什么是你的.baksed -i
Patrizio Bertoni,2015年

210

另一种有用的模式是:

sed -e 'script script' index.html > index.html.tmp && mv index.html.tmp index.html

不使用该-i选项,效果几乎相同,另外,这意味着,如果sed脚本由于某种原因失败,则不会破坏输入文件。此外,如果编辑成功,则不会留下任何备份文件。这种成语在Makefile中很有用。

很多sed都-i可以选择,但不是全部。posix sed并非如此。因此,如果您要实现可移植性,则最好避免这种情况。


9
+1表示没有备份文件,如果编辑失败,则不会破坏输入文件。在Mac上完美运行。
Mike Grace 2014年

完美地为我工作。谢谢!(在Mac上)
感兴趣的

1
这对我非常有效,在Ubuntu Server 14.04上sed -i一直将文件归零。
克里斯·吉丁斯

2
极其微小的增强:... && mv index.html{.tmp,}
EdwardGarson

5
@EdwardGarson确实,如果我键入它,那可能就是我要使用的-我同意它更整洁-但sh(如果我没记错的话)没有这种{...}扩展。在Makefile中,您可能会使用sh而不是bash,因此,如果要实现可移植性(或正性),则需要避免这种构造。
诺曼·格雷

95
sed -i 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' index.html

这将对文件index.html进行全局就地替换。引用字符串可以防止查询和替换中出现空格问题。


57

使用sed的-i选项,例如

sed -i bak -e s/STRING_TO_REPLACE/REPLACE_WITH/g index.html

这是什么意思?sed:-i不能与stdin一起使用
片状

2
如果您的模式包含空格,请记住将其括在引号中's/STRING_TO_REPLACE/REPLACE_WITH/g'
Doug Thompson

@sheetal:-i文件执行就地编辑,因此将其与stdin输入结合起来没有任何意义。
mklement0

这可能适用于macOS,但不适用于Arch Linux。
xdevs23 '17

如果没有-e,则接受的答案在MacOS Catalina上不起作用。使用-e可以正常工作。
cwhiii

18

要更改多个文件(并将每个文件的备份另存为* .bak):

perl -p -i -e "s/\|/x/g" *  

将把目录中的所有文件取而代之|x 这被称为“ Perl pie”(简单易用)


1
很高兴看到有人愿意看问题陈述,而不仅仅是标签。OP没有指定sed要求,仅将其用作已经尝试过的工具。
user7412956 '18


6
sed -i.bak "s#https.*\.com#$pub_url#g" MyHTMLFile.html

如果您有要添加的链接,请尝试此操作。搜索上面的URL(以https开头,以.com结尾),然后将其替换为URL字符串。我$pub_url在这里使用了一个变量。s这里表示搜索,g表示全局替换。

有用 !


6

警告:这是一种危险的方法!它滥用了Linux中的I / O缓冲区,并通过特定的缓冲区选项设法处理小文件。这是一个有趣的好奇心。但是不要在实际情况下使用它!

除了-i选项之外,sed 您还可以使用该tee实用程序

来自man

tee-从标准输入读取并写入标准输出和文件

因此,解决方案将是:

sed s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html | tee | tee index.html

-在这里tee重复以确保管道被缓冲。然后,管道中的所有命令都将被阻塞,直到它们得到一些输入以进行处理。当上游命令已将1个字节缓冲区(大小在某处定义)写入命令输入时,管道中的每个命令就会启动。因此,最后一个命令tee index.html(打开文件以写入并清空文件)将在上游管道完成并且输出在管道内的缓冲区中之后运行。

以下情况很可能不起作用:

sed s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html | tee index.html

-它将同时运行管道的两个命令,而不会发生任何阻塞。(不会阻塞管道应该由缓冲区传递由行而不是缓冲区的字节线。同样,你在运行时的cat | sed s/bar/GGG/。如果没有阻挡它的互动性更强,通常只有2命令的管道没有缓冲和阻挡运行,较长的管道中缓存。)的tee index.html意志打开文件进行写入,它将被清空。但是,如果您始终打开缓冲,则第二个版本也将起作用。


3
tee的输出文件也会立即打开,导致整个命令为空index.html。
sjngm

3
这将破坏任何大于管道缓冲区(通常为64KB)的输入文件。(@sjngm:文件不会像一样被立即截断>,但要指出的是,这是一个损坏的解决方案,很可能会导致数据丢失)。
mklement0

4

命令的问题

sed 'code' file > file

file在sed实际处理它之前被shell截断了。结果,您得到一个空文件。

sed执行此操作的方法是使用-i原位编辑,如其他答案所建议。但是,这并不总是您想要的。-i将创建一个临时文件,然后将其用于替换原始文件。如果您的原始文件是一个链接(此链接将被常规文件替换),则这会出现问题。如果需要保留链接,则可以使用临时变量存储sed的输出,然后再将其写回到文件中,如下所示:

tmp=$(sed 'code' file); echo -n "$tmp" > file

更好的是,使用printf而不是echo因为echo可能会\\\在某些shell 中一样处理(例如破折号):

tmp=$(sed 'code' file); printf "%s" "$tmp" > file

1
+1用于保留链接。它也可以用于临时文件:sed 'code' file > file.tmp; cat file.tmp > file; rm file.tmp
dashohoxha

3

ed答案:

printf "%s\n" '1,$s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' w q | ed index.html

为了重申codaddict的回答,shell 首先处理重定向,清除“ input.html”文件,然后 shell调用“ sed”命令,并向传递一个现在为空的文件。


2
快速问题,为什么人们不断给出答案的“ ed版本” sed?它执行得更快吗?
cregox

6
有些sed不实现-i就地编辑。ed是无处不在的,它确实使您可以将编辑内容保存到原始文件中。另外,套件中包含很多工具总是很不错的。
格伦·杰克曼(Glenn Jackman)2013年

嗯不错。因此,就性能而言,它们与我想的一样。谢谢!
cregox

2

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

ex -sc '%s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g|x' index.html
  1. % 选择所有行

  2. x 保存并关闭


0

我正在寻找可以定义行范围并找到答案的选项。例如,我想将host1从第36-57行更改为host2。

sed '36,57 s/host1/host2/g' myfile.txt > myfile1.txt

您也可以使用gi选项来忽略字符大小写。

sed '30,40 s/version/story/gi' myfile.txt > myfile1.txt

0

考虑到以上所有正确答案,使用“空运行”脚本总是一个好主意,这样您就不会损坏文件而不得不从头开始。

只需让您的脚本将输出溢出到命令行,而不是将其写入文件即可,例如:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

要么

less index.html | sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g 

这样,您可以查看并检查命令的输出,而不会截断文件。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.