sed编辑文件“就地”编辑文件

问题描述 投票:231回答:13

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

unix sed solaris
13个回答
357
投票

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

例:

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

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

在macOS上。


3
投票

If you are replacing the same amount of characters and after carefully reading “In-place” editing of files...

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

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 Reference Manual → 3.6.10 Opening File Descriptors for Reading and Writing

重定向运算符

[n]<>word

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


1
投票

您没有指定正在使用的shell,但是使用zsh可以使用=( )构造来实现此目的。有点像:

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

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


0
投票

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

find . -name pkg.json -print -exec sed -i '.bak' '1 s/^/version /' {} \;

0
投票

就像Moneypenny在Skyfall中所说的那样:“有时旧的方式是最好的。” Kincade后来说了类似的话。

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

快乐编辑到位。添加'\ nw \ n'来写入文件。对延迟回答请求表示歉意。


68
投票

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

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

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


46
投票

请注意,在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


21
投票

以下在我的Mac上工作正常

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

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

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

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

12
投票

有一点需要注意,sed不能自己编写文件,因为sed的唯一目的是充当“流”的编辑器(即stdin,stdout,stderr和其他>&n缓冲区,套接字等的管道)。考虑到这一点,您可以使用另一个命令tee将输出写回文件。另一种选择是创建一个补丁,将内容管道化为diff

Tee方法

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

补丁方法

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

更新:

另外,请注意补丁将从diff输出的第1行获取文件:

补丁不需要知道要访问哪个文件,因为这是在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

10
投票

sed做你想做的事是不可能的。甚至版本的sed支持用于编辑文件的-i选项也完全按照您明确声明不需要的内容执行:它们写入临时文件然后重命名该文件。但也许你可以使用ed。例如,要在文件foo中将所有出现的bar更改为file.txt,您可以执行以下操作:

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

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

但也许你应该考虑为什么你不想使用重命名的临时文件。即使您没有支持-ised,您也可以轻松编写脚本来为您完成工作。而不是sed -i 's/foo/bar/g' file,你可以做inline file sed 's/foo/bar/g'。写这样一个脚本是微不足道的。例如:

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

应该适合大多数用途。


8
投票

你可以使用vi

vi -c '%s/foo/bar/g' my.txt -c 'wq'

7
投票

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

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


3
投票
mv file.txt file.tmp && sed 's/foo/bar/g' < file.tmp > file.txt

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

© www.soinside.com 2019 - 2024. All rights reserved.