ChatGPT解决这个技术问题 Extra ChatGPT

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

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

为什么会这样?

Perl 替代方案:perl -pi -w -e 's/STRING_TO_REPLACE/REPLACE_WITH/g;' index.html
很多相关的 sed 命令来查找字符串并替换整行:stackoverflow.com/questions/11245144/…

t
tripleee

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

要解决此问题,您需要将 -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 是什么?
N
Norman Gray

另一种有用的模式是:

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),那么您需要避免这种结构。
R
Rich Apodaca
sed -i 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' index.html

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


K
Kevin

使用 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 它确实有效。
A
Alex

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

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

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


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

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


x
xealits

警告:这是一种危险的方法!它滥用 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:文件不会像 > 那样立即被截断,但重点是它是一个可能导致数据丢失的损坏解决方案)。
K
Kaey
sed -i.bak "s#https.*\.com#$pub_url#g" MyHTMLFile.html

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

有用 !


A
Andrzej Pronobis

命令的问题

sed 'code' file > file

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

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

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

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

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

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

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 版本”?它执行得更快吗?
一些 sed 没有实现 -i 来就地编辑。 ed 无处不在,可以让您将编辑保存到原始文件。另外,在您的工具包中拥有很多工具总是好的。
嗯不错。所以,在性能方面,我想它们是一样的。谢谢!
嗨,我知道这有点晚了,但我不能在这段代码中传递任何变量。比如,printf "%s\n" '1,$s/^STRING_TO_REPLACE.*/$MODPATH/g' w q | ed $SERVICESH > /dev/null 2>&1。我想将 $MODPATH 作为替换字符串传递,但无法使其工作
佚名

我正在寻找可以定义行范围并找到答案的选项。例如,我想将第 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

N
Nestor Milyaev

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

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

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 

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