我试图找出是否可以在单个 sed 命令中编辑文件,而无需 手动 将编辑后的内容流式传输到新文件中,然后将新文件重命名为原始文件名。我尝试了 -i
选项,但我的 Solaris 系统说 -i
是非法选项。有不同的方法吗?
-i
是 gnu sed 中的一个选项,但不在标准 sed 中。但是,它将内容流式传输到一个新文件,然后重命名该文件,因此它不是您想要的。
-i
option 将编辑后的内容流式传输到一个新文件中,然后在后台重命名它。
例子:
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
尽管这确实会创建一个临时文件,但它会替换原始文件,因为提供了一个空的就地后缀/扩展名。
sudo
时,单个命令胜过一系列命令。在我看来,问题在于如何避免手动创建和重命名临时文件,而无需安装最新的 GNU 或 BSD sed
。只需使用 Perl。
sed 's/foo/bar/g' file.txt > file.tmp && mv file.tmp file.txt
的内容。仅仅因为就地编辑使用临时文件进行重命名,并不意味着他/她必须在选项不可用时执行手动重命名。还有其他工具可以做他/她想做的事,而 Perl 是显而易见的选择。这怎么不是对原始问题的回答?
sed
实现他想要的。这个问题实际上是一个非常好的问题,但它的价值已经被那些无法阅读手册页或懒得实际上阅读 SO 上的问题的人所削弱。
请注意,在 OS X 上运行此命令时,您可能会遇到奇怪的错误,例如“无效的命令代码”或其他奇怪的错误。要解决此问题,请尝试
sed -i '' -e "s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g" <file>
这是因为在 sed
的 OSX 版本上,-i
选项需要一个 extension
参数,因此您的命令实际上被解析为 extension
参数,文件路径被解释为命令代码。来源:https://stackoverflow.com/a/19457213
brew install gnu-sed
和 PATH
将允许您使用 GNU 版本的 sed。感谢提及;一个明确的挠头。
以下在我的 Mac 上运行良好
sed -i.bak 's/foo/bar/g' sample
我们在示例文件中将 foo 替换为 bar 。原始文件的备份将保存在 sample.bak
要在没有备份的情况下编辑内联,请使用以下命令
sed -i'' 's/foo/bar/g' sample
需要注意的一点是,sed
不能自行编写文件,因为 sed 的唯一目的是充当“流”(即 stdin、stdout、stderr 和其他 >&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
/dev/stdin 2014-03-15
,1 月 7 日回答 - 你是时间旅行者吗?
/dev/stdin
不存在,因此您必须将 /dev/stdin
替换为 '-',一个不带引号的连字符,因此以下命令应该可以工作:$ sed 's/oo/u/' fubar | diff -p fubar -
和 $ sed 's/oo/u/' fubar | diff -p fubar - | patch
patch
是否也不会截断。我认为将输出保存到另一个文件然后 cat
-ing 到原始文件会更安全。
支持 -i
选项以就地编辑文件的 sed
版本写入临时文件,然后重命名该文件。
或者,您可以只使用 ed
。例如,要将文件 file.txt
中所有出现的 foo
更改为 bar
,您可以执行以下操作:
echo ',s/foo/bar/g; w' | tr \; '\012' | ed -s file.txt
语法类似于 sed
,但肯定不完全相同。
即使您没有 -i
支持 sed
,您也可以轻松编写脚本来为您完成这项工作。您可以使用 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
应该足以满足大多数用途。
inline
并如上所述调用它:inline inputfile sed 's/foo/bar/g'
ed
只回答真正到位的答案。我第一次需要ed
。谢谢你。一个小的优化是使用 echo -e ",s/foo/bar/g\012 w"
或 echo $',s/foo/bar/g\012 w'
,其中 shell 允许它避免额外的 tr
调用。
ed
并没有真正就地进行修改。从 strace
输出,GNU ed
创建一个临时文件,将更改写入临时文件,然后重写整个原始文件,保留硬链接。在 truss
输出中,Solaris 11 ed
也使用临时文件,但在使用 w
命令保存时将临时文件重命名为原始文件名,从而破坏任何硬链接。
ed
仍然保留硬链接。 YMMV
你可以使用 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 Reference Manual → 3.6.10 Opening File Descriptors for Reading and Writing:
重定向运算符 [n]<>word 会导致打开名称为 word 扩展的文件,以便在文件描述符 n 上读取和写入,如果未指定 n,则在文件描述符 0 上打开。如果文件不存在,则创建它。
就像 Moneypenny 在 Skyfall 中所说的那样:“有时老办法是最好的。”金凯德后来也说了类似的话。
$ printf ',s/false/true/g\nw\n' | ed {YourFileHere}
快乐编辑到位。添加了 '\nw\n' 来写入文件。为延迟回复请求道歉。
|
管道输入字符。 @MattMontag
\n
终止。 [范围]s/<old>/<new>/<flag>是替代命令的形式 - 在许多其他文本编辑器中仍然可以看到一个范例(好吧,vim,ed 的直接后代)无论如何,g
标志代表“全局”,意思是“每行上的每个实例你看”。范围 3,5
查看第 3-5 行。 ,5
查看 StartOfFile-5,3,
查看第 3-EndOfFile 行,,
查看 StartOfFile-EndOfFile。每行上的全局替换命令,用“true”替换“false”。然后,输入写命令w
。
您没有指定您使用的 shell,但使用 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 上解决这个问题,我必须在 this 之后向 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' {} \;
不定期副业成功案例分享
sed -i "s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g" <file>