394

我试图找出是否可以在单个 sed 命令中编辑文件,而无需手动将编辑的内容流式传输到新文件中,然后将新文件重命名为原始文件名。我尝试了该-i选项,但我的 Solaris 系统说这-i是一个非法选项。有不同的方法吗?

4

15 回答 15

647

-i选项将编辑的内容流式传输到一个新文件中,然后在幕后重命名它,无论如何。

例子:

sed -i 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' filename

在 macOS 上,您需要:

sed -i '' 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' filename
于 2012-10-02T18:39:16.783 回答
83

sed无法就地编辑文件的系统上,我认为更好的解决方案是使用perl

perl -pi -e 's/foo/bar/g' file.txt

尽管这确实会创建一个临时文件,但它会替换原始文件,因为提供了一个空的就地后缀/扩展名。

于 2012-10-03T02:01:06.437 回答
66

请注意,在 OS X 上运行此命令时,您可能会遇到奇怪的错误,例如“无效的命令代码”或其他奇怪的错误。要解决此问题,请尝试

sed -i '' -e "s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g" <file>

这是因为在 OSX 版本上sed,该-i选项需要一个extension参数,因此您的命令实际上被解析为extension参数,文件路径被解释为命令代码。来源:https ://stackoverflow.com/a/19457213

于 2015-01-29T03:48:59.690 回答
30

以下在我的 Mac 上运行良好

sed -i.bak 's/foo/bar/g' sample

我们在示例文件中将 foo 替换为 bar 。原始文件的备份将保存在 sample.bak

要在没有备份的情况下编辑内联,请使用以下命令

sed -i'' 's/foo/bar/g' sample
于 2014-07-03T09:14:41.513 回答
20

需要注意的一点sed是,sed 的唯一目的是充当“流”(即标准输入、标准输出、标准错误和其他>&n缓冲区、套接字等的管道)上的编辑器,因此不能自行编写文件。考虑到这一点,您可以使用另一个命令tee将输出写回文件。另一种选择是创建一个补丁,将内容通过管道传输到diff.

三通法

sed '/regex/' <file> | tee <file>

补丁方法

sed '/regex/' <file> | diff -p <file> /dev/stdin | patch

更新:

另外,请注意 patch 将使文件从 diff 输出的第 1 行更改:

Patch 不需要知道要访问哪个文件,因为它可以在 diff 输出的第一行中找到:

$ echo foobar | tee fubar

$ sed 's/oo/u/' fubar | diff -p fubar /dev/stdin
*** fubar   2014-03-15 18:06:09.000000000 -0500
--- /dev/stdin  2014-03-15 18:06:41.000000000 -0500
***************
*** 1 ****
! foobar
--- 1 ----
! fubar

$ sed 's/oo/u/' fubar | diff -p fubar /dev/stdin | patch
patching file fubar
于 2014-01-07T00:11:11.347 回答
16

该版本sed支持-i就地编辑文件的选项写入临时文件,然后重命名文件。

或者,您可以只使用ed. 例如,要更改文件中所有出现的footo ,您可以执行以下操作:barfile.txt

echo ',s/foo/bar/g; w' | tr \; '\012' | ed -s file.txt

语法类似于sed,但肯定不完全相同。

即使您没有-i支持sed,您也可以轻松编写脚本来为您完成工作。而不是sed -i 's/foo/bar/g' file,你可以做inline file sed 's/foo/bar/g'。这样的脚本编写起来很简单。例如:

#!/bin/sh
IN=$1
shift
trap 'rm -f "$tmp"' 0
tmp=$( mktemp )
<"$IN" "$@" >"$tmp" && cat "$tmp" > "$IN"  # preserve hard links

应该足以满足大多数用途。

于 2012-10-02T19:03:51.793 回答
14

你可以使用 vi

vi -c '%s/foo/bar/g' my.txt -c 'wq'
于 2017-06-30T09:04:34.590 回答
10

sed支持就地编辑。来自man sed

-i[SUFFIX], --in-place[=SUFFIX]

    edit files in place (makes backup if extension supplied)

示例

假设您有一个hello.txt包含以下文本的文件:

hello world!

如果要保留旧文件的备份,请使用:

sed -i.bak 's/hello/bonjour' hello.txt

您将得到两个文件:hello.txt内容:

bonjour world!

hello.txt.bak带有旧内容。

如果您不想保留副本,请不要传递扩展参数。

于 2015-07-17T21:55:19.920 回答
5

如果您要替换相同数量的字符并仔细阅读文件的“就地”编辑...

您还可以使用重定向运算符<>打开文件进行读写:

sed 's/foo/bar/g' file 1<> file

现场观看:

$ cat file
hello
i am here                           # see "here"
$ sed 's/here/away/' file 1<> file  # Run the `sed` command
$ cat file
hello
i am away                           # this line is changed now

来自Bash 参考手册 → 3.6.10 打开文件描述符以进行读写

重定向运算符

[n]<>word

导致打开名称为 word 扩展的文件,以便在文件描述符 n 上读取和写入,如果未指定 n,则在文件描述符 0 上打开。如果文件不存在,则创建它。

于 2016-08-25T11:26:01.103 回答
4

就像 Moneypenny 在 Skyfall 中所说的那样:“有时老办法是最好的。” 金凯德后来也说了类似的话。

$ printf ',s/false/true/g\nw\n' | ed {YourFileHere}

快乐编辑到位。添加了 '\nw\n' 来写入文件。为延迟回复请求道歉。

于 2017-05-17T18:44:20.043 回答
3

您没有指定您使用的是什么外壳,但是使用 zsh 您可以使用该=( )构造来实现这一点。类似于以下内容:

cp =(sed ... file; sync) file

=( )类似于但创建一个临时文件,该文件在终止>( )时会自动删除。cp

于 2012-10-02T19:09:48.560 回答
3
mv file.txt file.tmp && sed 's/foo/bar/g' < file.tmp > file.txt

应该保留所有硬链接,因为输出被引导回覆盖原始文件的内容,并且避免了对特殊版本的 sed 的任何需要。

于 2015-08-11T00:31:51.623 回答
1

很好的例子。我面临编辑许多文件的挑战,并且 -i 选项似乎是在 find 命令中使用它的唯一合理解决方案。这里的脚本在每个文件的第一行前面添加“版本:”:

find . -name pkg.json -print -exec sed -i '.bak' '1 s/^/version /' {} \;
于 2015-02-07T11:00:46.847 回答
1

为了在 Mac 上解决这个问题,我不得不在 core-utils 之后添加一些 unix函数

brew install grep
==> Caveats
All commands have been installed with the prefix "g".
If you need to use these commands with their normal names, you
can add a "gnubin" directory to your PATH from your bashrc like:
  PATH="/usr/local/opt/grep/libexec/gnubin:$PATH"

gsed用而不是调用sed。mac 默认不喜欢如何grep -rl显示带有预定义的文件名./

~/my-dir/configs$ grep -rl Promise . | xargs sed -i 's/Promise/Bluebirg/g'

sed: 1: "./test_config.js": invalid command code .

我还必须使用xargs -I{} sed -i 's/Promise/Bluebirg/g' {}名称中带有空格的文件。

于 2021-12-20T23:25:01.207 回答
0

如果要替换包含“/”的字符串,可以使用“?”。即将所有 *.py 文件的 '/usr/local/bin/python' 替换为 '/usr/bin/python3'。

寻找 。-name \*.py -exec sed -i 's?/usr/local/bin/python?/usr/bin/python3?g' {} \;

于 2021-12-26T01:48:34.900 回答