159

我对 Sed 一无所知,但需要这个命令(在 Ubuntu 上运行良好)才能在 Mac OSX 上运行:

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

我越来越:

sed: 1: "/etc/hosts": extra characters at the end of h command
4

4 回答 4

210

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

于 2013-05-25T03:15:19.100 回答
163

为了补充microtherion 的有用、中肯的答案

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

tl;博士

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

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

这是BSD/macOS sed命令:

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

对于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和 BSD/macOS之间所有差异的讨论,请参阅我的这个答案sedsed

便携式方法

注意:此处可移植意味着该命令适用于所讨论的两种实现。它在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中,执行其输入文件的就地更新[1]的选项 接受一个选项参数,该选项指定用于备份文件的后缀(文件扩展名)正在更新的文件-i

例如,在这两种实现中,以下将原始文件 , 保留file为备份文件file.bak

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

即使GNU sed中后缀参数是可选的,而在BSD/macOS sed中它是强制的,上述语法也适用于两种实现,因为直接将选项参数 ( ) 与选项 ( ) -相邻,而不是- 作为可选和强制选项参数.bak-i-i.bak-i .bak

  • 语法-i.bak唯一适用于可选选项参数的形式。
  • 语法-i.bak 可用作强制选项参数,作为 的替代方法,即分别-i .bak指定选项及其参数。

不指定后缀(通常是这种情况)意味着不应保留任何备份文件,这就是出现不兼容的地方:

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

  • 对于BSD/macOS sed,不指定后缀意味着将空字符串指定为 - 强制 - 后缀,并且出于技术原因,空字符串只能作为单独的参数传递:即-i '' not -i''

-i''不起作用,因为sed它与 just 无法区分-i,因为shell有效地删除了空引号(它使用句法函数连接和删除引号),并且-i在两种情况下都通过。''-i

(有效地)刚刚-i指定,下一个参数被解释为选项参数:

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

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

同样,使用:

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

-e被解释为后缀参数,而不是 Sed 的-e选项(如果需要,可以用来指定多个命令)。
结果,您获得了一个带有后缀的备份文件,而不是保留 NO 备份-e

该命令没有按预期工作并不那么明显,因为就地更新确实成功,因为该参数满足了后缀参数的语法要求-e

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


[1] 严格来说,是在后台创建一个临时文件,然后替换原始文件;这种方法可能会出现问题:请参阅我的这个答案的下半部分。

于 2017-07-01T18:10:07.840 回答
25

男人是你的朋友。

操作系统

 -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.
于 2013-05-25T03:14:34.300 回答
6

在 OS X 中,您可以使用 GNU 版本的 sed: 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
于 2017-06-30T00:36:17.093 回答