我试图找出是否可以在单个 sed 命令中编辑文件,而无需手动将编辑的内容流式传输到新文件中,然后将新文件重命名为原始文件名。我尝试了该-i
选项,但我的 Solaris 系统说这-i
是一个非法选项。有不同的方法吗?
15 回答
该-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
无法就地编辑文件的系统上,我认为更好的解决方案是使用perl
:
perl -pi -e 's/foo/bar/g' file.txt
尽管这确实会创建一个临时文件,但它会替换原始文件,因为提供了一个空的就地后缀/扩展名。
请注意,在 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
以下在我的 Mac 上运行良好
sed -i.bak 's/foo/bar/g' sample
我们在示例文件中将 foo 替换为 bar 。原始文件的备份将保存在 sample.bak
要在没有备份的情况下编辑内联,请使用以下命令
sed -i'' 's/foo/bar/g' sample
需要注意的一点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
该版本sed
支持-i
就地编辑文件的选项写入临时文件,然后重命名文件。
或者,您可以只使用ed
. 例如,要更改文件中所有出现的foo
to ,您可以执行以下操作:bar
file.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
应该足以满足大多数用途。
你可以使用 vi
vi -c '%s/foo/bar/g' my.txt -c 'wq'
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
带有旧内容。
如果您不想保留副本,请不要传递扩展参数。
如果您要替换相同数量的字符并仔细阅读文件的“就地”编辑...
您还可以使用重定向运算符<>
打开文件进行读写:
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 上打开。如果文件不存在,则创建它。
就像 Moneypenny 在 Skyfall 中所说的那样:“有时老办法是最好的。” 金凯德后来也说了类似的话。
$ printf ',s/false/true/g\nw\n' | ed {YourFileHere}
快乐编辑到位。添加了 '\nw\n' 来写入文件。为延迟回复请求道歉。
您没有指定您使用的是什么外壳,但是使用 zsh 您可以使用该=( )
构造来实现这一点。类似于以下内容:
cp =(sed ... file; sync) file
=( )
类似于但创建一个临时文件,该文件在终止>( )
时会自动删除。cp
mv file.txt file.tmp && sed 's/foo/bar/g' < file.tmp > file.txt
应该保留所有硬链接,因为输出被引导回覆盖原始文件的内容,并且避免了对特殊版本的 sed 的任何需要。
很好的例子。我面临编辑许多文件的挑战,并且 -i 选项似乎是在 find 命令中使用它的唯一合理解决方案。这里的脚本在每个文件的第一行前面添加“版本:”:
find . -name pkg.json -print -exec sed -i '.bak' '1 s/^/version /' {} \;
为了在 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' {}
名称中带有空格的文件。
如果要替换包含“/”的字符串,可以使用“?”。即将所有 *.py 文件的 '/usr/local/bin/python' 替换为 '/usr/bin/python3'。
寻找 。-name \*.py -exec sed -i 's?/usr/local/bin/python?/usr/bin/python3?g' {} \;