我试图找出是否可以在单个sed命令中编辑一个文件,而无需手动将编辑的内容流到一个新文件中,然后将新文件重命名为原始文件名。

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


当前回答

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

-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

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

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

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

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

即使没有-i支持sed,也可以很容易地编写一个脚本来完成这项工作。而不是sed -i 's/foo/bar/g'文件,你可以做内联文件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

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

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

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

您没有指定使用什么shell,但是对于zsh,您可以使用=()构造来实现这一点。大致如下:

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

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