sed就地标志,可在Mac(BSD)和Linux上使用


237

是否sed可以在Linux和Mac上调用无需备份的todo就地编辑功能?虽然sed似乎需要OS X附带的BSD sed -i '' …,但GNU sedLinux发行版通常随附将引号解释为空的输入文件名(而不是备份扩展名),并且需要sed -i …而是。

是否有任何两种语法都适用的命令行语法,所以我可以在两个系统上使用相同的脚本?


Perl不是一种选择吗?您必须使用sed吗?
Noufal Ibrahim 2011年

也许安装GNU版本并使用它!topbug.ne​​t/blog/2013/04/14/…?我会先尝试。再说一次,那也很烂。
德米特里·明科夫斯基2014年

2
@dimadima:对于其他浏览此问题的人来说,可能会感到有趣,因为他们的个人脚本在OS X计算机上已损坏。但是,就我而言,我需要一个开放源代码项目的构建系统,告诉您的用户首先安装GNU sed可能会违反此练习的初衷(以“无处不在的工作方式”修补一些文件) 。
dnadlinger 2014年

@klickverbot是的,很有道理。我首先添加评论作为答案,然后将其删除,意识到它不是您问题的答案:)。
德米特里·明科夫斯基2014年

1
交叉参考:如何使用sed -i(就地编辑)实现可移植性?在Unix和Linux Stack Exchange上。
MvG

Answers:


212

如果您真的只想使用sed -i“简单”的方法,那么以下DOES可以在GNU和BSD / Mac上运行sed

sed -i.bak 's/foo/bar/' filename

注意缺少空间和点。

证明:

# GNU sed
% sed --version | head -1
GNU sed version 4.2.1
% echo 'foo' > file
% sed -i.bak 's/foo/bar/' ./file
% ls
file  file.bak
% cat ./file
bar

# BSD sed
% sed --version 2>&1 | head -1
sed: illegal option -- -
% echo 'foo' > file
% sed -i.bak 's/foo/bar/' ./file
% ls
file  file.bak
% cat ./file
bar

显然,您可以删除这些.bak文件。


2
这是要走的路。添加一些字符,即可移植。删除备份文件很简单(调用时已经有了文件名sed
slezica 2015年

这在Mac上有效,但是在ubuntu 16.04上,它会将原始文件覆盖为0字节,并且还使用0字节创建了.bak文件?
房子9年

1
值得注意的是,在macOS Sierra(可能还有更早的版本,我没有电脑)上,sed使用时不接受位置command参数-i- 必须提供另一个标志-e。因此,该命令将变为:sed -i.bak -e 's/foo/bar/' filename
ELLIOTTCABLE

5
这将创建备份,而OP专门要求进行就地编辑。
马克-安德烈·Lafortune

8
因此,完整的解决方案是:sed -i.bak 's/foo/bar/' file && rm file.bak
joeytwiddle

111

这适用于GNU sed,但不适用于OS X:

sed -i -e 's/foo/bar/' target.file
sed -i'' -e 's/foo/bar/' target.file

这适用于OS X,但不适用于GNU sed:

sed -i '' -e 's/foo/bar/' target.file

在OS X上,您

  • 无法使用,sed -i -e因为备份文件的扩展名将设置为-e
  • 不能sed -i'' -e出于相同的原因使用-它-i和之间需要有一个空格''

1
@AlexanderMills告诉sed,下一个参数是命令-也可以在此处跳过。
本杰明W.

4
注意,在外壳解析时,-i-i''相同。sed 不能在这两个第一次调用上有不同的表现,因为它得到的参数完全相同。
wchargin

44

在OSX上,我总是通过Homebrew安装GNU sed版本,以避免脚本中的问题,因为大多数脚本都是针对GNU sed版本编写的。

brew install gnu-sed --with-default-names

然后,您的BSD sed将被GNU sed代替。

或者,您可以不使用默认名称进行安装,但是可以:

  • PATH安装后按照说明更改您的gnu-sed
  • 请检查您的脚本以在系统之间进行选择gsedsed取决于系统

4
--with-default-names从自制核取出,在更多信息这个答案。gnu-sed现在安装时,安装说明指定您需要添加gnubinPATHPATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"
pgericson

18

正如Noufal Ibrahim所问,为什么您不能使用Perl?任何Mac都将具有Perl,并且很少有Linux或BSD发行版在基本系统中不包含某些版本的Perl。可能实际上缺少Perl的仅有的环境之一就是BusyBox(其工作方式类似于GNU / Linux for -i,但是不能指定备份扩展名)。

按照ismail的建议,

由于perl随处可用,所以我只是这样做 perl -pi -e s,foo,bar,g target.file

在几乎所有情况下,这似乎都是比脚本,别名或其他解决方案更好的解决方案,以解决sed -iGNU / Linux与BSD / Mac之间的根本不兼容问题。


我喜欢这个解决方案。perl是Linux标准基本规范的一部分,因为5月1日,2009年refspecs.linuxfoundation.org/LSB_4.0.0/LSB-Languages/...
OregonTrail

1
这很容易实现最佳便携性
ecnepsnai

17

没有办法使其起作用。

一种方法是使用类似以下的临时文件:

TMP_FILE=`mktemp /tmp/config.XXXXXXXXXX`
sed -e "s/abc/def/" some/file > $TMP_FILE
mv $TMP_FILE some/file

这对两个都有效


是否存在SOME_FILE =“ $(sed -s” s / abc / def /“ some / file)”的原因;echo“ $ SOME_FILE”>一些/文件;不工作,而不是
贾森-格罗斯

看起来您将受到bash变量的最大大小的限制,不是吗?不确定它将与GBs文件一起使用。
类似的产品

3
如果要创建一个临时文件,为什么不给扩展名一个备份文件(然后可以将其删除),如下面@kine所示?
Alex Dupuy 2014年

13

答:不可以。

最初接受的答案实际上并不满足要求(如注释中所述)。(我在查找a file-e在目录中“随机”出现的原因时找到了这个答案。)

显然,无法sed -i同时在MacOS和Linuces上正常工作。

对于我来说,值得的建议不是使用就地更新sed(具有复杂的故障模式),而是生成新文件并随后对其重命名。换句话说:避免-i


9

-i选件不是POSIX Sed的一部分。一种更可移植的方法是在Ex模式下使用Vim:

ex -sc '%s/alfa/bravo/|x' file
  1. % 选择所有行

  2. s 更换

  3. x 保存并关闭


这是正确的答案。POSIX的主要功能是可移植性。
Kajukenbo

9

这是另一个可在Linux和macOS上运行的版本,无需使用eval也无需删除备份文件。它使用Bash数组存储sed参数,比使用eval以下方法更干净:

# Default case for Linux sed, just use "-i"
sedi=(-i)
case "$(uname)" in
  # For macOS, use two parameters
  Darwin*) sedi=(-i "")
esac

# Expand the parameters in the actual call to "sed"
sed "${sedi[@]}" -e 's/foo/bar/' target.file

这不会创建备份文件,也不会创建带有引号的文件。


2
这是正确的答案。我把它简化一下,但这个想法完美的作品 sedi=(-i) && [ "$(uname)" == "Darwin" ] && sedi=(-i '') sed "${sedi[@]}" -e 's/foo/bar/' target.file
亚历Skrypnyk

好东西!不过,我更喜欢较长版本的可读性。
nwinkler '19

1
这是我与不同系统兼容的最佳方法。
Victor Choy

6

史蒂夫·鲍威尔的答案很正确,请查阅OS X和Linux(Ubuntu 12.04)上的sed的MAN页面,着重强调了这两种操作系统在“就地” sed使用中的内部兼容性。

JFYI,使用Linux版本的sed -i和任何引号(表示空文件扩展名)之间不应有空格,因此

sed Linux手册页

#Linux
sed -i"" 

sed OSX手册页

#OSX (notice the space after the '-i' argument)
sed -i "" 

我通过在bash'if'中使用别名命令和OS名称输出' uname '来解决这个问题。解释引号时,尝试将依赖于OS的命令字符串存储在变量中时会遇到麻烦。为了扩展/使用脚本中定义的别名,必须使用' shopt -s expand_aliases '。shopt的用法在这里处理。


1

如果您需要sedbash脚本中就地执行操作,并且不希望就地使用.bkp文件生成结果,并且有一种方法可以检测到os(例如,使用ostype.sh),则-使用bash内置的shell进行以下破解eval应该可以:

OSTYPE="$(bash ostype.sh)"

cat > myfile.txt <<"EOF"
1111
2222
EOF

if [ "$OSTYPE" == "osx" ]; then
  ISED='-i ""'
else # $OSTYPE == linux64
  ISED='-i""'
fi

eval sed $ISED 's/2222/bbbb/g' myfile.txt
ls 
# GNU and OSX: still only myfile.txt there

cat myfile.txt
# GNU and OSX: both print:
# 1111
# bbbb

# NOTE: 
# if you just use `sed $ISED 's/2222/bbbb/g' myfile.txt` without `eval`,
# then you will get a backup file with quotations in the file name, 
# - that is, `myfile.txt""`

0

您可以使用海绵。Sponge是一个古老的unix程序,可在moreutils软件包中找到(在ubuntu和debian中都可以找到,在mac的homebrew中都可以找到)。

它将缓冲管道中的所有内容,等待直到管道关闭(可能意味着输入文件已经关闭),然后覆盖:

手册页

概要

sed'...'文件| grep'...'| 海绵锉


3
好的方法–不幸的是,在最初的情况下并没有真正的帮助,因为诉诸sed的原因是要在客户端计算机上使用的跨平台帮助程序脚本中使用某些东西,而您只能依赖系统以外的任何东西正在安装的工具。但是可能对其他人有帮助。
dnadlinger

0

以下内容适用于Linux和OS X:

sed -i' ' <expr> <file>

例如对于f包含aaabbaaba

sed -i' ' 's/b/c/g' f

产量aaaccaaca在Linux和Mac。请注意,有一个带引号的字符串,其中包含空格,并且和字符串之间没有空格-i。单引号或双引号都可以。

在Linux上,我使用bashUbuntu 14.04.4下的4.3.11版本,在OS X 10.11.4 El Capitan(Darwin 15.4.0)下的Mac 3.2.57版本上。


1
哇,我很高兴我没有听完上面所有的人说这是不可能的,并继续阅读!这确实有效。谢谢!
人,

4
我在Mac OS不工作...创建与空间新文件附加到文件名末尾
弗雷德里克·

在Mac(/ usr / bin / sed)上也对我不起作用-我现在有一个额外的备份文件,文件名后附加一个空格:-(
paul_h

尝试从单引号中删除空格,它对我有用。
dps

0

我遇到了这个问题。唯一快速的解决方案是将mac中的sed替换为gnu版本:

brew install gnu-sed

这重现了现有答案,并提供了2015
以来的
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.