【问题标题】:sed command with -i option (in-place editing) works fine on Ubuntu but not Mac [duplicate]带有 -i 选项的 sed 命令(就地编辑)在 Ubuntu 上运行良好,但在 Mac 上运行良好 [重复]
【发布时间】:2013-05-20 16:55:47
【问题描述】:

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

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

我明白了:

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

【问题讨论】:

  • @AnthonySottile:建议的副本肯定是密切相关的,除了 - 与这个问题不同 - 它特别要求一个与 both BSD/macOS 和 GNU 实现一起使用的解决方案的sed。因此,对 BSD/macOS- 解决方案感兴趣的人可能会在这里找到更集中的答案。
  • @mklement0 是的,问题略有不同,但两者的答案几乎相同——我不值得有两个问题耸耸肩跨度>
  • @AnthonySottile:这只是一个建议——无论哪种方式都可以——让未来的投票者决定吧。
  • 可能,BashX 项目可以帮助您解决此类问题。

标签: linux macos sed bsd inplace-editing


【解决方案1】:

男人是你的朋友。

操作系统

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 所以只要在 sed -i 后面加上单引号, sed -i '1i export PATH="$HOME/.composer/vendor/bin:$PATH"' $HOME/.bashrc 还是不行为我工作
    • @pal4life 关键是你需要一个 separate 引号集合before 你开始命令,所以像sed -i '' '1i export PATH="$HOME/.composer/vendor/bin:$PATH"' $HOME/.bashrc
    • 如果您需要相同的脚本同时在 macOS(OS X、BSD)和 Linux 等平台上工作,那么您必须使用备份后缀(例如 .bak)并且必须将其附加到-i 选项 — sed -i.bak …。如果您不这样做,脚本将无法在两个平台之一上正确运行。 GNU sed 不喜欢 BSD sed 需要就地编辑的空参数;您不能将空参数附加到-i,因为它是空的并且与-i 无法区分。此外,由于相互矛盾的设计,-i 选项不会被 POSIX 标准化为-i;它无法调和不可调和的。
    • 哦,如果您不想要备份,请在运行 sed 后运行 rm -f /etc/hosts.bak。这比尝试找出您已安装的sed 的哪个变体或我见过的其他小玩意要简单得多。我希望您将文件保持在版本控制之下(或以其他方式备份),以便在您的 sed 脚本中存在错误时可以恢复。当然,.bak 文件可以为您提供这种保护。
    • 可能,BashX 项目可以帮助您解决此类问题。
    【解决方案3】:

    在 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
    

    【讨论】:

    • 这是救了我的人。我升级了我的安装并将 /usr/local/opt/gnu-sed/libexec/gnubin 添加到我的路径中。我在 OSX 10.12.6 上。
    【解决方案4】:

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

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

    tl;dr

    相当于这个 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

    便携方法

    注意:这里的可移植意味着该命令适用于所讨论的两种实现。它在 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] 其输入文件,接受一个 option-argument,它指定要更新的文件的 备份文件 使用什么 后缀(文件扩展名)

    例如,在 both 实现中,以下将原始文件 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,上述语法也适用于两种实现,因为直接将选项参数(.bak)与选项(@ 987654351@) - -i.bak,而不是 -i .bak - 既可以作为可选也可以作为强制选项参数

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

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

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

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

    -i'' 不起作用,因为对于 sed,它与 -i 无法区分,因为 shell 有效地删除 空引号(它连接 -i'' 并使用语法函数删除引号),并且在 两种情况 中只传递 -i

    只要(有效地)指定了-inext 参数被解释为选项参数:

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

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

    类似地,有:

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

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

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

    这些备份文件的意外创建很容易被忽视是Crt's incorrect answerthis incorrect answer to a similar question 获得如此多的赞成票的最可能解释(截至撰写本文时)。


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

    【讨论】:

    • 这是对该问题的正确和最有用的答案。不确定是谁投了反对票,但没有表达反对的理由,而我有很多理由接受这一点而不是其他答案。
    • 我不是这里的专家,但是,为了保持 POSIX 合规性,mv file file.bak && sed 's/foo/bar/' file.bak > file && rm file.bak 可以工作吗?
    • @CássioRenan:原则上是的,但是sed 's/foo/bar' file > file.bak && mv file.bak file(如one linked answer所示)更简单。如果-i 可用并且可移植性不是问题,则最好使用它,因为它会努力保留原始文件的属性,尽管它仍然会破坏符号链接 - 请参阅another linked answer 的第二半。符合 POSIX 的方法不仅存在符号链接问题,而且只是创建了一个具有默认权限的新文件,...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-16
    相关资源
    最近更新 更多