将sed 's/c/d/' myFile的输出重定向到myFile中

21

我在脚本中使用sed进行替换操作,我希望替换后的文件可以覆盖原文件。通常情况下,我认为你应该使用以下方法:

% sed -i 's/cat/dog/' manipulate
sed: illegal option -- i

然而,正如您所见,我的sed命令并没有那个命令。

我尝试过这个:

% sed 's/cat/dog/' manipulate > manipulate

但是这样只会将 manipulate 变成一个空文件(有道理)。

这种方法可行:

% sed 's/cat/dog/' manipulate > tmp; mv tmp manipulate

我想知道是否有一种标准的方法将输出重定向到与输入相同的文件中。


3
为什么没有人提到“sponge”? - Elazar Leibovich
2
我猜这个问题不值得一提。 - ixe013
2020年的一个电话:为什么sed 's/cat/dog/' manipulate > manipulate会生成一个空文件? - Evgeny
10个回答

40

我通常使用第三种方法,但有一个重要的变化:

sed 's/cat/dog/' manipulate > tmp && mv tmp manipulate

即将;更改为&&,以便移动仅在sed成功时发生; 否则,一旦您在sed语法中犯了一个错别字,您就会失去原始文件。

注意! 对于那些阅读标题并错过了OP的约束"我的sed不支持-i"的人来说:对于大多数人来说,sed将支持-i,所以最好的方法是:

sed -i 's/cat/dog/' manipulate


sed -i ... 在幕后实际上只是 sed 's/cat/dog/' manipulate > tmp && mv tmp manipulate 的简化形式。参考链接:https://unix.stackexchange.com/questions/276651/sed-with-inplace-editing-changes-group-ownership-of-file。 - Andrew Henle

8

是的,在FreeBSD/MacOSX中,sed也支持-i选项,但需要将空字符串作为参数以就地编辑文件。

sed -i "" 's/old/new/g' file   # FreeBSD sed

-i 不需要空字符串。该参数的值是用于备份的扩展名,当它为空字符串时 - 将不进行备份。在 GNU sed 上,-i 的默认值为空字符串。 - bartimar
1
这是我的救命稻草。 - Jianxin Gao

7
如果您不想移动副本,可以使用ed编辑器:
ed file.txt <<EOF
%s/cat/dog/
wq
EOF

ed支持所有sed支持的正则表达式吗?特别是范围? - sixtyfootersdude
1
ed支持所有sed支持的正则表达式,包括范围。ed - 适用于文件 sed - 适用于流另一个主要区别是,默认情况下,ed命令只能在文件的当前行上工作(这就是为什么我编辑了我的示例并添加了%,以使命令在所有行上运行),而sed命令默认情况下针对所有行运行。 - amertune
你也可以使用“ex”,它是“vim”的别名。它非常丰富多彩。 - romaninsh

3
肯恩和派克在《Unix编程艺术》一书中讨论了这个问题。他们的解决方案是编写一个名为overwrite的脚本,从而使这样的事情成为可能。
使用方法是:overwrite file cmd file
# overwrite: copy standard input to output after EOF

opath=$PATH
PATH=/bin:/usr/bin

case $# in
0|1)   echo 'Usage: overwrite file cmd [args]' 1>&2; exit 2
esac

file=$1; shift
new=/tmp/overwr1.$$; old=/tmp/overwr2.$$
trap 'rm -f $new $old; exit 1' 1 2 15  # clean up

if PATH=$opath "$@" >$new
then
       cp $file $old           # save original
       trap '' 1 2 15          # wr are commmitted
       cp $new $file
else
       echo "overwrite: $1 failed, $file unchanged" 1>&2
       exit 1
fi
rm -f $new $old

一旦您将上述脚本添加到$PATH中,您可以执行以下操作:

overwrite manipulate sed 's/cat/dog/' manipulate

为了让您的生活更加便利,您可以使用同一本书中的replace脚本:
# replace: replace  str1 in files with str2 in place
PATH=/bin:/usr/bin

case $# in
    0|2) echo 'Usage: replace str1 str2 files' 1>&2; exit 1
esac

left="$1"; right="$2"; shift; shift

for i
do
    overwrite $i sed "s@$left@$right@g" $i
done

如果您的$PATH中也包含replace,那么您可以使用以下命令:

replace cat dog manipulate

1
在这种情况下,您可以从www.sunfreeware.com下载一个与-i选项兼容的新版本的sed ;) - Anders
+1,覆盖脚本非常有用(根据个人喜好进行了一些更改后)! - Arkku
1
@Anders:用非标准替代品替换系统sed可能不是可行的选择,即使它是免费的,也不能回答如何使用可用/标准工具的问题。 - Arkku
POSIX标准 - Solaris遵循的标准 - 仅规定了符合要求的sed选项的最小集合,因此GNU / Sed或您喜欢称之为的任何工具实际上都符合您所谓的“标准工具”。 - Anders
1
@Anders,它符合POSIX标准,但如果您使用非POSIX选项,则当然不依赖于标准工具。当然可以在系统上安装GNU sed,但可能无法这样做。 - Alok Singhal

3
你可以使用来自moreutilssponge
sed "s/cat/dog/" manipulate | sponge manipulate

对我来说,这是最好的答案。 - acgbox

1
也许 -i 是 gnu sed,或者只是旧版本的 sed,但不管怎样,你已经在正确的轨道上了。第一个选项可能是最常见的选项,第三个选项是如果你想让它在任何地方工作(包括太阳能机器)... :)这些都是“标准”的做法。

在FreeBSD/MacOSX的sed中也支持-i - Isaac

1

要更改多个文件(并将每个文件保存为*.bak的备份):

perl -p -i -e“s / oldtext / newtext / g” *

replaces any occurence of oldtext by newtext in all files in the current folder. However you will have to escape all perl special characters within oldtext and newtext using the backslash 

This is called a “Perl pie” (mnemonic: easy as a pie)
The -i flag tells it do do in-place replacement, and it should be ok to use single (“'”) as well as double (“””) quotes.

    If using ./* instead of just *, you should be able to do it in all sub-directories 
See man perlrun for more details, including how to take a backup file of the original.
using sed:
            sed -i    's/old/new/g' ./*  (used in GNU)
    sed -i '' 's/old/new/g' ./*  (used in FreeBSD)

0

-i选项在标准的sed中不可用。

您的替代方案是第三种方式或者使用perl


-1
很多答案,但都不正确。这里是正确且最简单的答案:
$ echo "111 222 333" > file.txt
$ sed -i -s s/222/444/ file.txt 
$ cat file.txt
111 444 333
$ 

2
该用户明确表示,他使用的版本 sed 认为 -i 标志是“非法”的。因此不能使用... - arkascha

-2

使用打开文件句柄的解决方法:

exec 3<manipulate 

防止打开的文件被截断:

rm manipulate
sed 's/cat/dog/' <&3 > manipulate

1
虽然从技术上讲这是一个巧妙的技巧,但这是危险的。如果在sed执行期间你的电源突然中断,你将会失去数据。 - Gilles 'SO- stop being evil'
在移除之前,只需将链接放在其他位置,就可以了。 - JB.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接