使用-i选项(就地编辑)的sed命令在Ubuntu上工作正常,但在Mac上工作正常

时间:2013-05-25 03:03:08

标签: linux macos sed bsd inplace-editing

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

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

我得到了:

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

4 个答案:

答案 0 :(得分:136)

Ubuntu附带GNU sed,其中-i选项的后缀是可选的。 OS X附带BSD sed,后缀是必需的。试试sed -i ''

答案 1 :(得分:62)

补充microtherion's helpful, to-the-point answer

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

<强> 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 sed和BSD / macOS sed之间差异的讨论,请参阅我的this answer

便携式方法

注意: Portable 此处表示该命令适用于所讨论的两种实现。它不能在 POSIX 意义上移植,因为the -i option is not POSIX-compliant

# 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标准的解决方案,请参阅我的this related answer

背景资料

GNU sed(大多数Linux发行版的标准)和 BSD / macOS sed -i选项,执行就地更新 [1]  其输入文件,接受选项参数,指定要用于正在更新的文件的备份文件后缀(文件扩展名)

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

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

即使with GNU sed the suffix argument is optional,而BSD/macOS sed it is mandatory,上述语法也适用于两个实现,因为直接连接了option-argument(.bak )选项(-i) - -i.bak,而不是-i .bak - 可用作可选必需选项-argument

  • 语法-i.bak唯一的表单,适用于可选选项参数。
  • 语法-i.bak 作为必需选项参数,作为替代-i .bak,即,指定选项及其参数单独

不指定后缀 - 通常就是这种情况 - 意味着应该保留无备份文件,这就是出现不兼容的地方

  • 使用 GNU sed,不指定后缀意味着仅使用-i

  • 使用 BSD / macOS sed,未指定后缀意味着将空字符串指定为 - mandatory - suffix,并出于技术的原因,空字符串只能作为单独的参数传递:即-i '' 不是 -i''

-i''无法正常工作,因为sed-i无法区分,因为 shell 有效地删除空引号(它连接-i''并删除带语法功能的引号),并且只传递中的-i 案件。

在(有效)指定-i时, next 参数被解释为option-argument:

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被解释为后缀参数,而不是Sed的-e选项(可用于指定多个命令,如果需要的话) 因此,而不是保留NO备份,您将获得带有后缀-e的备份文件。

由于-e参数满足后缀参数的语法要求,因此该命令不能按预期工作,因此不太明显,因为就地更新确实成功。

这些备份文件的意外创建很容易被忽视,这是Crt's incorrect answerthis incorrect answer to a similar question收到这么多上选票的最可能的解释(截至撰写本文时)。

[1]严格地说,在幕后创建一个临时文件,然后替换原始文件;这种方法可能会有问题:请参阅我this answer的下半部分。

答案 2 :(得分:21)

男人是你的朋友。

OS X

 -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.

答案 3 :(得分:3)

在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