有没有一种在Linux和Mac上都能够进行原地编辑而不备份的sed
调用方法?虽然OS X附带的BSD sed
似乎需要sed -i '' …
,但通常随Linux发行版一起提供的GNU sed
会将引号解释为空输入文件名(而不是备份扩展名),因此需要使用sed -i …
。
是否有任何命令行语法可以在两种系统上同时使用,以便我可以在两个系统上使用相同的脚本?
有没有一种在Linux和Mac上都能够进行原地编辑而不备份的sed
调用方法?虽然OS X附带的BSD sed
似乎需要sed -i '' …
,但通常随Linux发行版一起提供的GNU sed
会将引号解释为空输入文件名(而不是备份扩展名),因此需要使用sed -i …
。
是否有任何命令行语法可以在两种系统上同时使用,以便我可以在两个系统上使用相同的脚本?
如果你真的想要用 sed -i
来达到“简单”的目的,以下方法在 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
文件。
-i
调用时,sed
不接受位置参数command
—— 必须提供另一个标志-e
。因此,命令变成了:sed -i.bak -e 's/foo/bar/' filename
。 - ELLIOTTCABLEsed -i.bak 's/foo/bar/' 文件 && rm 文件.bak
。 - joeytwiddlekinefilebak
而不是kinefile.bak
。只要您使用rm $1bak
而不是rm $1.bak
,它就可以正常工作。(当然,xyz.bak
文件在ls
列表中更加明显,*.bak
比*bak
更自然,如果文件名在$f
中,则必须说"${f}bak"
而不是更简单的"$f.bak"
)。 - G-Man Says 'Reinstate Monica'这个在 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''
在Shell解析时是相同的;因为sed
获得了完全相同的参数,所以它无法在这两个最初的调用上表现出不同的行为。 - wchargin在OSX上,我总是通过Homebrew安装GNU sed版本,以避免在脚本中出现问题,因为大多数脚本都是为GNU sed版本编写的。
brew install gnu-sed --with-default-names
然后您的BSD sed将被GNU sed替换。
或者,您可以安装没有default-names选项,但是:
gnu-sed
后,请根据说明更改您的PATH
gsed
或sed
,具体取决于您的系统。--with-default-names
已从Homebrew-Core中删除,更多信息请参见此答案。现在在安装gnu-sed
时,安装说明指定您需要将gnubin
添加到您的PATH
中:PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"
。 - pgericson-i
,它的工作方式类似于GNU/Linux,但无法指定备份扩展名)。在几乎所有情况下,这似乎比脚本、别名或其他解决方案更好地处理由于perl无处不在,我只需执行
perl -pi -e s,foo,bar,g target.file
sed -i
在GNU/Linux和BSD/Mac之间的根本不兼容性。perl
已成为Linux标准基础规范的一部分。http://refspecs.linuxfoundation.org/LSB_4.0.0/LSB-Languages/LSB-Languages/perllocation.html - OregonTrail以下是另一个版本,可在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
注:此代码行旨在将文件中所有出现的“foo”替换为“bar”。该代码行包括一个检查操作系统类型的条件以及根据不同类型的操作系统使用不同的sed
参数来兼容MacOS和Linux。 - Alex Skrypnyksed
(例如使用MacPorts或Brew),则此方法将失败。 - undefined回答: 不行。
原本被接受的答案实际上并没有达到要求(如评论所述)。 (我在寻找一个解释一个file-e
在我的目录中“随机”出现的原因时发现了这个答案。)
显然无法让 sed -i
在 MacOS 和 Linux 上都可靠地工作。
我的建议,不管价值多少,是不要使用 sed
来进行原地更新(具有复杂的错误模式),而是生成新文件并在之后重命名它们。 换句话说:避免使用 -i
。
无法让它正常工作。
一种方法是使用临时文件,例如:
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" > some/file;
- Jason GrossSteve Powell's answer非常正确,查阅OSX和Linux(Ubuntu 12.04)上的sed MAN页面强调了两个操作系统中“原地”使用sed时的不兼容性。
顺便说一下,在Linux版本的sed中,-i和任何引号之间不应该有空格(这表示空文件扩展名),因此:
#Linux
sed -i""
和
#OSX (notice the space after the '-i' argument)
sed -i ""
可在 GNU 系统和 OSX 上使用的便携脚本:
if [[ $(uname) == "Darwin" ]]; then
SP=" " # Needed for portability with sed
fi
sed -i${SP}'' -e "s/foo/bar/g" -e "s/ping/pong/g" foobar.txt
"Darwin"
周围加引号 - 它只能解析为它本身。而如果不是因为[[ ]]
的特殊语义,你需要在$(uname)
周围加引号以确保正确处理所有可能的返回值。 - Charles Duffy