是否sed
可以在Linux和Mac上调用无需备份的todo就地编辑功能?虽然sed
似乎需要OS X附带的BSD sed -i '' …
,但GNU sed
Linux发行版通常随附将引号解释为空的输入文件名(而不是备份扩展名),并且需要sed -i …
而是。
是否有任何两种语法都适用的命令行语法,所以我可以在两个系统上使用相同的脚本?
是否sed
可以在Linux和Mac上调用无需备份的todo就地编辑功能?虽然sed
似乎需要OS X附带的BSD sed -i '' …
,但GNU sed
Linux发行版通常随附将引号解释为空的输入文件名(而不是备份扩展名),并且需要sed -i …
而是。
是否有任何两种语法都适用的命令行语法,所以我可以在两个系统上使用相同的脚本?
Answers:
如果您真的只想使用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
文件。
sed
)
sed
使用时不接受位置command
参数-i
- 必须提供另一个标志-e
。因此,该命令将变为:sed -i.bak -e 's/foo/bar/' filename
sed -i.bak 's/foo/bar/' file && rm file.bak
这适用于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
和之间需要有一个空格''
。-i
和-i''
相同。sed
不能在这两个第一次调用上有不同的表现,因为它得到的参数完全相同。
在OSX上,我总是通过Homebrew安装GNU sed版本,以避免脚本中的问题,因为大多数脚本都是针对GNU sed版本编写的。
brew install gnu-sed --with-default-names
然后,您的BSD sed将被GNU sed代替。
或者,您可以不使用默认名称进行安装,但是可以:
PATH
安装后按照说明更改您的gnu-sed
gsed
或sed
取决于系统正如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 -i
GNU / Linux与BSD / Mac之间的根本不兼容问题。
perl
是Linux标准基本规范的一部分,因为5月1日,2009年refspecs.linuxfoundation.org/LSB_4.0.0/LSB-Languages/...
没有办法使其起作用。
一种方法是使用类似以下的临时文件:
TMP_FILE=`mktemp /tmp/config.XXXXXXXXXX`
sed -e "s/abc/def/" some/file > $TMP_FILE
mv $TMP_FILE some/file
这对两个都有效
这是另一个可在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
这不会创建备份文件,也不会创建带有引号的文件。
sedi=(-i) && [ "$(uname)" == "Darwin" ] && sedi=(-i '')
sed "${sedi[@]}" -e 's/foo/bar/' target.file
史蒂夫·鲍威尔的答案很正确,请查阅OS X和Linux(Ubuntu 12.04)上的sed的MAN页面,着重强调了这两种操作系统在“就地” sed使用中的内部兼容性。
JFYI,使用Linux版本的sed -i和任何引号(表示空文件扩展名)之间不应有空格,因此
#Linux
sed -i""
和
#OSX (notice the space after the '-i' argument)
sed -i ""
我通过在bash'if'中使用别名命令和OS名称输出' uname '来解决这个问题。解释引号时,尝试将依赖于OS的命令字符串存储在变量中时会遇到麻烦。为了扩展/使用脚本中定义的别名,必须使用' shopt -s expand_aliases '。shopt的用法在这里处理。
如果您需要sed
在bash
脚本中就地执行操作,并且不希望就地使用.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""`
您可以使用海绵。Sponge是一个古老的unix程序,可在moreutils软件包中找到(在ubuntu和debian中都可以找到,在mac的homebrew中都可以找到)。
它将缓冲管道中的所有内容,等待直到管道关闭(可能意味着输入文件已经关闭),然后覆盖:
从手册页:
概要
sed'...'文件| grep'...'| 海绵锉
以下内容适用于Linux和OS X:
sed -i' ' <expr> <file>
例如对于f
包含aaabbaaba
sed -i' ' 's/b/c/g' f
产量aaaccaaca
在Linux和Mac。请注意,有一个带引号的字符串,其中包含空格,并且和字符串之间没有空格-i
。单引号或双引号都可以。
在Linux上,我使用bash
Ubuntu 14.04.4下的4.3.11版本,在OS X 10.11.4 El Capitan(Darwin 15.4.0)下的Mac 3.2.57版本上。