【问题标题】:Find and replace in file and overwrite file doesn't work, it empties the file在文件中查找和替换并覆盖文件不起作用,它会清空文件
【发布时间】:2011-07-07 12:18:23
【问题描述】:

我想通过命令行对 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是文件的内容,已经执行了查找和替换。

为什么会这样?

【问题讨论】:

标签: shell unix sed io-redirection


【解决方案1】:

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

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

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

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

【讨论】:

  • truncates the file 而不是 opens the file 可能会更清楚。
  • 至少在我的 Mac 上,第一个建议不起作用...如果您要对文件进行就地替换,则必须指定扩展名。不过,您至少可以传入一个长度为零的扩展名:sed -i '' s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html
  • 对于变量 sed -i.bak 's/'$search'/'$replace'/g' index.html
  • 在 osx 上,使用空字符串 '' 作为 -i 的参数,例如:sed -i '' 's/blah/xx/g'
  • 但是在sed -i之后你的.bak是什么?
【解决方案2】:

使用 sed 的 -i 选项,例如

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

【讨论】:

  • 这是什么意思? sed: -i 不能与标准输入一起使用
  • 如果你的模式包含空格,记得用引号括起来 - 's/STRING_TO_REPLACE/REPLACE_WITH/g'
  • @sheetal: -i 执行 files 的就地编辑,因此将其与 stdin 输入结合起来没有意义。
  • 这可能适用于 macOS,但不适用于我的 Arch Linux。
  • 如果没有 -e,接受的答案在 MacOS、Catalina 上不起作用。使用 -e 它确实有效。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    另一种有用的模式是:

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

    在不使用-i 选项的情况下,效果大致相同,另外还意味着,如果 sed 脚本由于某种原因失败,输入文件不会被破坏。此外,如果编辑成功,则不会留下任何备份文件。这种习惯用法在 Makefile 中很有用。

    很多 sed 都有 -i 选项,但不是全部; posix sed 不是。因此,如果您的目标是便携性,则最好避免。

    【讨论】:

    • +1 表示没有备份文件,如果编辑失败,也不会破坏输入文件。在 mac 上完美运行。
    • 完美地为我工作。谢谢! (在 Mac 上)
    • 这对我来说非常适合在 Ubuntu Server 14.04 sed -i 一直将文件归零的地方。
    • 极小的增强:... && mv index.html{.tmp,}
    • @EdwardGarson 确实,如果我输入它,我可能会使用它——我同意它更整洁——但sh(如果我没记错的话)没有{...} 扩展。在 Makefile 中,您可能使用 sh 而不是 bash,因此如果您的目标是可移植性(或 posixness),那么您需要避免这种结构。
    【解决方案5】:

    还有ed 的回答:

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

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

    【讨论】:

    • 快速提问,为什么人们一直给出sed 的“ed 版本”答案?它执行得更快吗?
    • 一些seds 没有实现-i 就地编辑。 ed 无处不在,可以让您将编辑保存到原始文件中。另外,在您的工具包中拥有很多工具总是很好的。
    • 好的,很酷。所以,在性能方面,我想它们是一样的。谢谢!
    • 嗨,我知道这有点晚了,但我不能在这段代码中传递任何变量。喜欢,printf "%s\n" '1,$s/^STRING_TO_REPLACE.*/$MODPATH/g' w q | ed $SERVICESH > /dev/null 2>&1。我想将 $MODPATH 作为替换字符串传递,但无法使其正常工作
    【解决方案6】:

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

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

    将获取目录中的所有文件并将|替换为x 这被称为“Perl pie”(很简单)

    【讨论】:

    • 很高兴看到有人愿意查看问题陈述,而不仅仅是标签。 OP 没有将sed 指定为要求,仅将其用作已经尝试过的工具。
    【解决方案7】:

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

    除了sed-i 选项 您可以使用 tee 实用程序

    来自man

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

    所以,解决办法是:

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

    -- 这里重复tee 以确保管道被缓冲。然后管道中的所有命令都被阻塞,直到它们得到一些输入来处理。当上游命令将 1 个字节缓冲区(大小定义为somewhere)写入命令的输入时,管道中的每个命令都会启动。所以最后一个命令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 将打开文件进行写入,它将被清空。但是,如果您始终打开缓冲,第二个版本也可以工作。

    【讨论】:

    • tee 的输出文件也立即打开,导致整个命令的 index.html 为空。
    • 这将损坏任何大于管道缓冲区(通常为 64KB)的输入文件。 (@sjngm:文件不会像> 那样立即被截断,但重点是它是一个可能导致数据丢失的损坏解决方案。
    【解决方案8】:
    sed -i 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' index.html
    

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

    【讨论】:

      【解决方案9】:

      命令的问题

      sed 'code' file > file
      

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

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

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

      更好的是,使用printf 而不是echo,因为echo 可能在某些shell(例如dash)中将\\ 处理为\

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

      【讨论】:

      • +1 用于保留链接。它也适用于临时文件:sed 'code' file > file.tmp; cat file.tmp > file; rm file.tmp
      【解决方案10】:
      sed -i.bak "s#https.*\.com#$pub_url#g" MyHTMLFile.html
      

      如果您有要添加的链接,请尝试此操作。搜索上述 URL(此处以 https 开头,以 .com 结尾)并将其替换为 URL 字符串。我在这里使用了变量$pub_urls 这里是搜索,g 是全局替换。

      有效!

      【讨论】:

        【解决方案11】:

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

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

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

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

        【讨论】:

          【解决方案12】:

          在充分尊重上述正确答案的情况下,“试运行”这样的脚本始终是一个好主意,这样您就不会损坏文件并且必须从头开始。

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

          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 
          

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2018-12-05
            • 1970-01-01
            • 2012-04-08
            • 2012-10-14
            • 1970-01-01
            相关资源
            最近更新 更多