Sed编辑文件到位

我试图找出是否有可能在单个sed命令中编辑一个文件,而没有手动<强> < / >强流编辑的内容到一个新文件,然后将新文件重命名为原始文件名。

我尝试了-i选项,但我的Solaris系统说-i是一个非法选项。还有别的办法吗?

410275 次浏览

-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

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

或者,你也可以直接使用ed。例如,要将文件file.txt中所有出现的foo更改为bar,你可以这样做:

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

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

即使你没有一个支持sed-i,你也可以很容易地编写一个脚本来为你做这项工作。你可以用inline file sed 's/foo/bar/g'代替sed -i 's/foo/bar/g' file。这样的脚本编写起来很简单。例如:

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

对于大多数用途应该是足够的。

你没有指定你使用的shell,但是在zsh中你可以使用=( )构造来实现这一点。大致如下:

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

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

在一个系统中,sed没有编辑文件的能力,我认为更好的解决方案是使用perl:

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

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

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

三通法

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

修补方法

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

更新:

另外,请注意patch将从diff输出的第一行获取要更改的文件:

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

下面的程序在我的mac上运行良好

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

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

要编辑内联而不备份,请使用以下命令

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

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

非常好的例子。我在编辑许多文件时遇到了挑战,而-i选项似乎是在find命令中使用它的唯一合理解决方案。下面的脚本在每个文件的第一行前面添加“version:”:

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

sed支持就地编辑。从man sed:

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


edit files in place (makes backup if extension supplied)

例子:

假设你有一个文件__abc0,其文本为:

hello world!

如果你想保留旧文件的备份,使用:

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

你最终会得到两个文件:hello.txt,其内容为:

bonjour world!

hello.txt.bak的旧内容。

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

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

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

如果您正在替换相同数量的字符,并且在仔细阅读“就地”编辑文件后…

你也可以使用重定向操作符<>打开文件进行读写:

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或文件描述符0上进行读写 如果未指定n。如果该文件不存在,则创建该文件

就像Moneypenny在《Skyfall》里说的:“有时候旧的方法是最好的。” Kincade后来说过类似的话。

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

快乐编辑到位。 增加了'\nw\n'来写入文件。

你可以使用vi

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

为了解决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' {}

如果你想替换包含'/'的sting,你可以使用'?'。例如,将所有*.py文件的“/usr/local/bin/python”替换为“/usr/bin/python3”。

找到。/usr/bin/python ?/usr/bin/python3?G ' {} \;