sed命令在文件中查找和替换并覆盖文件不起作用,它会清空文件

BBa*_*les 582 unix shell sed io-redirection

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

我的命令看起来像这样:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html > index.html
Run Code Online (Sandbox Code Playgroud)

当我运行它并在之后查看该文件时,它是空的.它删除了我的文件的内容.

当我再次恢复文件后运行它:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html
Run Code Online (Sandbox Code Playgroud)

stdout是文件的内容,并且已执行查找和替换.

为什么会这样?

cod*_*ict 893

shell> index.html在命令行中看到 它打开文件index.html进行写入时,擦除其以前的所有内容.

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

sed -i.bak s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html
Run Code Online (Sandbox Code Playgroud)

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

  • 在osx上,使用空字符串''作为-i的参数,如:`sed -i''s/blah/xx/g'` (29认同)
  • 说`截断文件`而不是`打开文件`可能会让它更清晰. (20认同)
  • 至少在我的Mac上,第一个建议不起作用......如果你在文件上进行就地替换,你必须指定一个扩展名.你至少可以传入一个零长度的扩展名:sed -i's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html (12认同)
  • 对于变量sed -i.bak的''$ search'/'$ replace'/ g'index.html (5认同)
  • 但是`sed -i`之后你的`.bak`是什么? (4认同)
  • 按照python的哲学,“显式比隐式更好”,这里使用的是完全相同但更易读的命令版本:`sed --in-place“ .bak” s / STRING_TO_REPLACE / STRING_TO_REPLACE_IT / g index.html` (2认同)

Nor*_*ray 206

另一种有用的模式是:

sed -e 'script script' index.html > index.html.tmp && mv index.html.tmp index.html
Run Code Online (Sandbox Code Playgroud)

这有很多相同的效果,不使用该-i选项,另外意味着,如果sed脚本由于某种原因失败,输入文件不会被破坏.此外,如果编辑成功,则没有剩余的备份文件.这种习惯用法在Makefile中很有用.

相当多的seds有-i选择权,但不是全部; posix sed是一个没有的.因此,如果您的目标是可移植性,那么最好避免使用.

  • 没有备份文件的+1,如果编辑失败,则不会破坏输入文件.在mac上完美地工作. (8认同)
  • @EdwardGarson的确,这可能就是我在输入时使用的东西 - 我同意它更整洁 - 但是`sh`(如果我没记错的话)没有`{...}`扩展.在Makefile中你可能使用`sh`而不是`bash`,所以如果你的目标是可移植性(或posixness),那么你需要避免这种结构. (5认同)
  • 非常小的增强:`... && mv index.html {.tmp,}` (2认同)

Ric*_*aca 90

sed -i 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' index.html
Run Code Online (Sandbox Code Playgroud)

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


Kev*_*vin 57

使用sed的-i选项,例如

sed -i bak -e s/STRING_TO_REPLACE/REPLACE_WITH/g index.html
Run Code Online (Sandbox Code Playgroud)

  • 适用于我.接受的答案没有 (5认同)
  • 如果它包含空格,请记住用引号括起您的模式 - "s/STRING_TO_REPLACE/REPLACE_WITH/g" (2认同)

Ste*_*emo 18

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

perl -p -i -e "s/\|/x/g" *  
Run Code Online (Sandbox Code Playgroud)

将在目录中的所有文件,并替换|x 这就是所谓的"Perl的馅饼"(容易,因为馅饼)

  • 很高兴看到有人愿意查看问题陈述,而不仅仅是标签。OP 没有指定“sed”作为要求,仅将其用作已经尝试过的工具。 (2认同)

小智 14

您应该尝试使用该选项-i进行就地编辑.


xea*_*its 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
Run Code Online (Sandbox Code Playgroud)

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

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

sed s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html | tee index.html
Run Code Online (Sandbox Code Playgroud)

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

  • tee的输出文件也会立即打开,从而导致整个命令的索引为空。 (3认同)
  • **这会_损坏_大于_管道缓冲区_(通常为64KB)的任何输入文件**。(@sjngm:该文件不会像`>`那样被立即截断,但要指出的是,这是一个损坏的解决方案,很可能导致数据丢失)。 (3认同)

Kae*_*aey 6

sed -i.bak "s#https.*\.com#$pub_url#g" MyHTMLFile.html
Run Code Online (Sandbox Code Playgroud)

如果您要添加链接,请尝试此操作.按上述方式搜索URL(以https开头,此处以..com结尾)并将其替换为URL字符串.我$pub_url在这里使用了一个变量.s这里意味着搜索和g意味着全球替代.

有用 !


And*_*bis 5

命令的问题

sed 'code' file > file
Run Code Online (Sandbox Code Playgroud)

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

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

tmp=$(sed 'code' file); echo -n "$tmp" > file
Run Code Online (Sandbox Code Playgroud)

更好的是,使用 useprintf而不是echosinceecho可能会\\\在某些 shell 中一样进行处理(例如 dash):

tmp=$(sed 'code' file); printf "%s" "$tmp" > file
Run Code Online (Sandbox Code Playgroud)

  • +1用于保留链接。它还可以使用临时文件: `sed 'code' file > file.tmp; cat 文件.tmp > 文件;rm 文件.tmp` (2认同)