Sed 命令 with-i 选项(就地编辑)在 Ubuntu 上运行良好,但在 Mac 上就不行了

我对 Sed 一无所知,但是我需要这个命令(它在 Ubuntu 上工作得很好)来在 Mac OSX 上工作:

sed -i "/ $domain .*#drupalpro/d" /etc/hosts

我得到了:

sed: 1: "/etc/hosts": extra characters at the end of h command
114730 次浏览

男人是你的朋友。

操作系统

 -i extension
Edit files in-place, saving backups with the specified extension.
If a zero-length extension is given, no backup will be saved.  It
is not recommended to give a zero-length extension when in-place
editing files, as you risk corruption or partial content in situ-
ations where disk space is exhausted, etc.

Ubuntu 附带 GNUsed,其中 -i选项的后缀是可选的。OS X 附带 BSDsed,后缀是强制性的。试试 sed -i ''

在 OS X 中,可以使用 sed 的 GNU 版本: gsed

# if using brew
brew install gnu-sed


#if using ports
sudo port install gsed

然后,如果您的脚本应该是可移植的,那么根据您的操作系统,您可以定义使用哪个命令。

SED=sed
unamestr=`uname`
if [[ "$unamestr" == "Darwin" ]] ; then
SED=gsed
type $SED >/dev/null 2>&1 || {
echo >&2 "$SED it's not installed. Try: brew install gnu-sed" ;
exit 1;
}
fi
# here your sed command, e.g.:
$SED -i "/ $domain .*#drupalpro/d" /etc/hosts

作为 麦克瑟里昂的回答很有帮助的补充:

  • 便携式解决方案
  • 背景资料

Tl; dr :

相当于这个 < em > GNU sed(大多数 Linux发行版的标准)命令:

sed -i    's/foo/bar/' file

是这个 < em > BSD/macOS sed命令:

sed -i '' 's/foo/bar/' file  # Note the '' as a *separate argument*

对于 < em > BSD/macOS sed,以下命令 做工作完全或不按预期执行:

sed -i    's/foo/bar/' file  # Breaks; script is misinterpreted as backup-file suffix
sed -i''  's/foo/bar/' file  # Ditto
sed -i -e 's/foo/bar/' file  # -e is misinterpreted as backup-file suffix

有关 所有的讨论 GNU sed和 BSD/macOS sed之间的区别,请参阅我的 这个答案

便携式方法 :

注意: 这里的 便携式的意味着该命令同时适用于所讨论的两种实现。它在 POSIX意义上是不可移植的,因为 -i选项不兼容 POSIX

# Works with both GNU and BSD/macOS Sed, due to a *non-empty* option-argument:
# Create a backup file *temporarily* and remove it on success.
sed -i.bak 's/foo/bar/' file && rm file.bak

有关详细说明,请参阅下文; 有关其他解决方案,包括兼容 POSIX 的解决方案,请参阅我的 这个相关的答案


背景资料

GNU sed(大多数 Linux 发行版的标准)和 BSD/macOS sed中,< strong > -i选项,执行 就地更新 < sup > [1] 接受一个 选择-论点选择-论点,指定要更新的文件的 备份文件使用哪个 后缀(文件扩展名)。译注:

例如,在 都有实现中,以下代码将原始文件 file作为备份文件 file.bak:

sed -i.bak 's/foo/bar/' file  # Keep original as 'file.bak'; NO SPACE between -i and .bak

尽管使用 对于 < em > GNU sed,后缀参数是 < em > 可选的 ,而使用 < em > BSD/macOS sed它是 < em > 强制性 ,上面的语法仍然适用于 都有实现,因为 直接连接选项参数(ABC2)到选项(ABC3)-ABC4,而不是 -i .bak-作为 < em > 可选 和 < em > 强制 选项参数:

  • 语法 -i.bak是用于 可以选择选项参数的 只有表单。
  • 语法 -i.bak 还有作为 强制性的选项参数,作为 另一种选择-i .bak,即指定选项及其参数 分开

没有指定后缀 ——这种情况经常发生——意味着应该保留 没有备份文件,这就是 不相容的情况出现了:

  • 对于 GNU sed,不指定后缀意味着只使用 -i 自己

  • 对于 BSD/macOS sed,没有指定后缀意味着指定 空字符串作为-强制-后缀,而指定 由于 < em > technology 的原因,空字符串只能作为一个 < em > 单独的 参数传递: 即 -i '' 没有 -i''

-i''不起作用,因为对于 sed来说,它与 -i 无法区分,因为 Shell实际上是 移除的空引号(它连接 -i'',并通过语法函数删除引号) ,在 都有的情况下只传递 -i

如果(实际上)只指定了 -i,那么 下一个参数就被解释为选项参数:

sed -i 's/foo/bar/' file # BREAKS with BSD/macOS Sed

's/foo/bar/'-用于 Sed 剧本(命令)-现在被解释为 后缀,单词 file被解释为脚本。
将这样的单词解释为脚本将导致模糊的错误消息,如
sed: 1: "file": invalid command code f,
因为 f被解释为 Sed 命令(函数)。

类似地,用:

sed -i -e 's/foo/bar/' file # CREATES BACKUP FILE 'file-e'

-e被解释为 后缀参数,NOT 被解释为 Sed 的 -e选项 (如果需要,可用于指定 多个命令)。
因此,< strong > 不保留 NO 备份,而是获得一个后缀为 -e的备份文件。

这个命令不能按预期的方式工作,这是不太明显的,因为考虑到后缀参数的语法要求由 -e参数满足,就地更新是成功的。

这些备份文件的意外创建很容易被忽视,这是 CRT 回答错误对类似问题的不正确答案收到如此多赞成票(截至本文撰写时)的最可能的解释。


[1]严格地说,一个临时文件是在幕后创建的,然后 取代是原始文件; 这种方法可能会有问题: 请参见我的 这个答案的下半部分。