在OS X上使用sed进行原地编辑

256

我想在OS X上使用sed编辑一个文件。 我正在使用以下命令:

sed 's/oldword/newword/' file.txt

输出被发送到终端。 file.txt 没有被修改。 使用此命令将更改保存到 file2.txt

sed 's/oldword/newword/' file1.txt > file2.txt

但是我不想创建另一个文件,我只想编辑file1.txt。我该怎么做?

我尝试过使用-i标志,但会出现以下错误:

sed: 1: "file1.txt": invalid command code f

3
您尝试使用“-i”标志时使用的确切命令是什么? - eldarerathis
8个回答

434

你可以通过为备份文件提供后缀来正确使用-i标志。扩展你的例子:

sed -i.bu 's/oldword/newword/' file1.txt

将为您提供两个文件:一个名为file1.txt的文件,其中包含替换后的内容,另一个名为file1.txt.bu的文件,其中包含原始内容。

有一定危险性

如果您想要毁坏性地覆盖原始文件,请使用类似以下的命令:

sed -i '' 's/oldword/newword/' file1.txt
      ^ note the space

由于该行的解析方式,选项标志和其参数之间需要有空格,因为参数长度为零。

除了可能破坏原始内容之外,我不知道任何通过这种方式欺骗sed的进一步危险性。 但应该注意,如果此次调用sed是脚本的一部分,则Unix Way™(仅个人意见)是使用非破坏性的sed,测试其是否清洁地退出,然后才删除多余的文件。


根据 sed 手册,如果你的设备存储空间不足,可能会在处理文件时出现中断并导致错误的输出结果。如果你正在使用本地源代码控制,则通常情况下可以不备份使用 sed -i "" 命令(或者在运行 -i 模式的 sed 命令之前执行 git init && git add -A . && git commit -m 'backup')。 - cfeduke
8
我发现其中稍微危险的部分特别有用。+1 - Andre
10
BSD的sed命令要求在-i标志后面不能有空格。因此,-i''是有效的,但-i ''是无效的。 - Todd A. Jacobs
4
啊!BSD的sed命令忽略了'\n'字符并打印出'n'来!我要在我的Mac上将BSD的sed替换为GNU的sed! - Nicholas Terry
我一直在编写脚本,这些脚本既可以在GIT Bash(Windows)下运行,也可以在Mac上运行。 测试这些脚本的人在Mac上发现,使用-i ''选项进行原地编辑时,会创建备份文件,并以双引号结尾,这真是奇怪! - undefined
显示剩余4条评论

35

-i 标志可能对您无效,因为您遵循了 GNU sed 的示例,而 macOS 使用 BSD sed,它们具有稍微不同的语法。

所有其他答案都告诉您如何更正语法以与 BSD sed 兼容。另一种选择是在您的 macOS 上安装 GNU sed:

brew install gsed

然后使用它代替 macOS 附带的 sed 版本(注意 g 前缀),例如:

gsed -i 's/oldword/newword/' file1.txt

如果您希望GNU sed命令在macOS上始终具有可移植性,可以将"gnubin"目录添加到您的路径中,方法是将以下内容添加到您的.bashrc/.zshrc文件中(运行brew info gsed以查看确切需要执行的操作):

export PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"

从那时起,GNU sed 成为您的默认 sed,您可以简单地运行:

sed -i 's/oldword/newword/' file1.txt

1
我已经在我的shell配置中将sed别名设置为gsed。这样做是有效的。你的解决方案有什么优势吗? - Niels Bom

26

我也遇到了类似的MacOS问题

sed -i '' 's/oldword/newword/' file1.txt

不工作,但是

sed -i"any_symbol" 's/oldword/newword/' file1.txt

工作良好。


6
现在情况反过来了。sed -i '' <file> 可以工作,但 sed -i'' <file> 不再起作用。 - Zohaib Amanzai

9
sed -i -- "s/https/http/g" file.txt

当我在Mac上尝试时,会出现一个错误,提示"sed:-i不能与stdin一起使用"。 - Christopher Bradshaw
4
在我的macOS(10.15.6)上,“--”似乎可以工作,但实际上它会创建一个“file.txt--”备份文件,必须手动删除,因此这个解决方案并不比已接受的解决方案更好。而且,“--”是具有误导性的,好像该选项的行为类似于处理方式不同的GNU工具。 - anol

5
您可以使用已经提到的-i''--in-place)选项来进行sed编辑。请参阅:in-place参数中的-i,但请注意,-i选项是非标准的FreeBSD扩展,可能在其他操作系统上不可用。其次,sed是一个流编辑器,而不是文件编辑器
另一种方法是在Vim Ex模式下使用内置的替换功能,例如:
$ ex +%s/foo/bar/g -scwq file.txt

对于多个文件:

$ ex +'bufdo!%s/foo/bar/g' -scxa *.*

要递归编辑所有文件,可以使用**/*.*(如果shell支持,请通过shopt -s globstar启用)。另一种方法是使用gawk和它的新的“inplace”扩展,如下所示:
$ gawk -i inplace '{ gsub(/foo/, "bar") }; { print }' file1

4

这会创建备份文件。例如,对于我来说,sed -i -e 's/hello/hello world/' testfile 会在相同的目录下创建一个名为testfile-e的备份文件。


在 macOS Catalina 上也是一样。分别传递 -i 和 -e(不是 -ie 一起!)会创建一个带有 '-e' 后缀的备份文件。 - Daniel Kesner

2

您可以使用:

sed -i -e 's/<string-to-find>/<string-to-replace>/' <your-file-path>

例子:

sed -i -e 's/Hello/Bye/' file.txt

这在Mac上运行得非常顺畅。


8
这会在我使用Mac OS Catalina时创建一个带有'-e'附加的备份文件。 - Daniel Kesner
8
在 macOS 中,这是错误的。请查看那里的 "man sed" 页面。-i 需要一个参数。由于您没有指定参数,它使用 -e,结果会多出一个带有 -e 的第二个文件。在 macOS 中,不存在 -e,而是 -E。因此,在 macOS 中,正确的命令是 sed -i '' -E 's/Hello/Bye/' file.txt - Marc J. Schmidt
在我的情况下,有一个小的变化可以使用。我用以下命令替换了两个不同的字符串(我想你可以添加更多): sed -i'' -e 's/_tools/tools/' -e 's/_static/static/' test.txt - Albert
@MarcJ.Schmidt这并不是“错”的,而是一个好的方法,可以在Mac和Linux上运行相同的脚本。与在不同平台上维护不同的脚本相比,备份文件的成本是可承受的;而且找到并删除这样的备份文件也很容易。 - Yijun Blue Yuan

0
如果你需要替换多个不同的单词:
sed -i '' -e 's/_tools/tools/' -e 's/_static/static/' test.txt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接