带有 -i 选项的 sed 命令在 Mac 上失败,但在 Linux 上有效

Posted

技术标签:

【中文标题】带有 -i 选项的 sed 命令在 Mac 上失败,但在 Linux 上有效【英文标题】:sed command with -i option failing on Mac, but works on Linux 【发布时间】:2011-05-13 22:07:35 【问题描述】:

我已成功使用以下sed 命令在 Linux 中搜索/替换文本:

sed -i 's/old_link/new_link/g' *

但是,当我在我的 Mac OS X 上尝试它时,我得到:

“命令 c 需要 \ 后跟文本”

我以为我的 Mac 运行的是普通的 BASH shell。怎么了?

编辑:

根据@High Performance,这是由于 Mac sed 具有不同的 (BSD) 风格,因此我的问题是如何在 BSD sed 中复制此命令?

编辑:

这是一个导致此问题的实际示例:

sed -i 's/hello/gbye/g' *

【问题讨论】:

这意味着sed 将数据中的“c”视为命令。你在使用变量吗?请发布更接近实际命令和您正在处理的一些数据的内容。您可以通过 echo x | sed c 来简单演示此错误。 @Dennis,上面的简单命令会导致这种情况,尽管它正在处理的数据是整个网站(我正在转换所有图像链接),包括 html 和 css 文件... 【参考方案1】:

您的 Mac 确实运行 BASH shell,但这更多的是您正在处理的 sed 实现的问题。在 Mac 上,sed 来自 BSD,与您在典型 Linux 机器上可能找到的 sed 略有不同。我建议你man sed

【讨论】:

感谢您指出 BSD 问题 - 但我是 sed 文盲,只需要快速修复我的命令 - 快速浏览一下 man 并没有告诉我任何信息 @Yarin -- 不,如果我快速浏览一下 man sed,那也不会告诉您任何信息。试试看更长的时间。 大多数 SO 的答案都埋藏在一个人的某个地方,但这就是 SO 的意义——需要聪明人回答的忙碌的人【参考方案2】:

如果您使用 -i 选项,您需要为您的备份提供扩展名。

如果你有:

File1.txt
File2.cfg

该命令(注意-i''-e 之间缺少空格以使其在新版本的Mac 和GNU 上工作):

sed -i'.original' -e 's/old_link/new_link/g' *

创建 2 个备份文件,例如:

File1.txt.original
File2.cfg.original

没有可移植的方法来避免制作备份文件,因为不可能找到适用于所有情况的混合 sed 命令:

sed -i -e ... - 不适用于 OS X,因为它会创建 -e 备份 sed -i'' -e ... - 不适用于 OS X 10.6,但适用于 10.9+ sed -i '' -e ... - 不适用于 GNU

注意鉴于并非所有平台都可以使用 sed 命令,您可以尝试使用其他命令来达到相同的效果。

例如,perl -i -pe's/old_link/new_link/g' *

【讨论】:

我遇到了同样的问题。感谢您提供此解决方案。但是在我尝试使用“man sed”来查找“-i”的描述的地方,没有任何关于使用 -i '' 忽略备份的内容。这是我的第一个责备。其次,当出现错误“命令需要\后跟文本”时,为什么不直接告诉我们它需要选项'-i'的备份名称!!??这样的事情无处不在:你得到一个错误,但不知道为什么会出错,然后你搜索手册,它什么也没解释。然后你谷歌它找到其他人也有同样的问题。我的意思是,为什么不在手册中举例呢? @lukmac 据sed 所知,您确实提供了备份后缀。备份后缀为s/old_link/new_link/g。之后的下一个参数应该是编辑命令。因为它将命令解释为备份名称,所以它将第一个文件名作为编辑命令,但它们无效。 这会一直是个问题吗? Apple 是否可以通过某种方式使用 OSX 创建解决方法/软件包 GNU sed?或者,GNU sed 不能支持sed -i '' -e ... sed -i'' -e 在 mac 10.14 上似乎无法正常工作 sed -i -- ... 似乎工作正常。还提到了@***.com/a/50245014/619961【参考方案3】:

我相信在 OS X 上,当您使用 -i 时,备份文件的扩展名是必需的。试试:

sed -i .bak 's/hello/gbye/g' *

使用 GNU sed 扩展是可选的

【讨论】:

【参考方案4】:

或者,您可以在您的 Mac 中安装 GNU 版本的 sed,称为 gsed,并使用标准 Linux 语法使用它。

为此,通过运行sudo port install gsed 使用端口安装gsed(如果没有,请在http://www.macports.org/ 获取它)。然后,你可以运行sed -i 's/old_link/new_link/g' *

【讨论】:

.. 或者如果您使用自制软件,请安装 gnu-sed 感谢@Sudar,点赞!【参考方案5】:

Sinetris 的回答是正确的,但我将它与 find 命令一起使用,以更具体地了解我想要更改的文件。一般来说,这应该可以工作(在 osx /bin/bash 上测试):

find . -name "*.smth" -exec sed -i '' 's/text1/text2/g'  \;

一般来说,在复杂项目中使用sed 而不使用find 效率较低。

【讨论】:

-exec 非常好!我只是想知道最后的斜线是否真的需要 @YeLiu: 如果没有 \ ,当我尝试这样时,; 会被 shell 解释【参考方案6】:

这适用于 GNU 和 BSD 版本的 sed:

sed -i'' -e 's/old_link/new_link/g' *

或有备份:

sed -i'.bak' -e 's/old_link/new_link/g' *

注意-i 选项后缺少空格! (对于 GNU sed 是必需的)

【讨论】:

第一个在 OSX 上不起作用(我刚刚在 10.6.8 上测试过) 对于我在 OS X (10.10.3) 上的我来说,第一个创建了后缀为 -e 的备份文件。不好。第二个是唯一在 Ubuntu 和 OS X 之间对我有用的东西。不过我不想要备份文件,所以我必须在删除它之后立即运行 rm 命令。 第一行应该用空格重写,以便在 10.10 上工作:sed -i'' ... => sed -i '' ... @DanielJomphe 但是在 GNU sed 上添加这个空间不起作用 @marcin 第一个在 OSX 10.11.2 上也适用于我。唯一的问题是它会创建一个备份文件,之后需要将其删除。第二个看起来更好,因为我们以后不必弄清楚文件名。【参考方案7】:
sed -ie 's/old_link/new_link/g' *

使用 gnu sed 在 BSD 和 Linux 上工作

【讨论】:

这会在 linux 上创建另一个带有 e 的文件名 坏了,最好去掉。 它在 Mac 和 Linux 上运行。这个解决方案有什么问题? 不,它不适用于 Mac BSD Sed - 它会创建一个备份文件,并在文件名中添加“e”。【参考方案8】:

在 Mac 上遇到同样的问题,用brew 解决了:

brew install gnu-sed

并用作

gsed SED_COMMAND

您也可以将sed 设置为gsed 的别名(如果需要):

alias sed=gsed

【讨论】:

您为什么将same answer 准确地指定为Ohad Kravchick? 这样设置别名不是个好主意 而不是别名,如相应 brew 页面中建议的那样,将其添加到路径:PATH="$(brew --prefix)/opt/gnu-sed/libexec/gnubin:$PATH" 因为我们都是懒惰的开发者,所以将这一行添加到你的~/.zshrc 或bashrc 文件中:export PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH" 然后运行source ~/.zshrc 更改为你选择的rc 文件!然后运行which sed 来检查它是否被改变了。【参考方案9】:

正如其他答案所表明的那样,没有办法在不制作备份文件的情况下在 OS X 和 Linux 上可移植地使用 sed。所以,我改为使用这个 Ruby 单行代码:

ruby -pi -e "sub(/ $/, '')" ./config/locales/*.yml

在我的例子中,我需要从 rake 任务中调用它(即,在 Ruby 脚本中),所以我使用了这个额外的引用级别:

sh %qruby -pi -e "sub(/ $/, '')" ./config/locales/*.yml

【讨论】:

【参考方案10】:

以下是如何将环境变量应用到模板文件(无需备份)。

1。使用 FOO 创建模板以供以后替换。

echo "Hello FOO" > foo.conf.tmpl

2。将 FOO 替换为 FOO 变量并输出到新的 foo.conf 文件

FOO="world" && sed -e "s/FOO/$FOO/g" foo.conf.tmpl > foo.conf

同时使用 ma​​cOS 10.12.4Ubuntu 14.04.5

【讨论】:

【参考方案11】:

我创建了一个函数来处理 MacOS(在 MacOS 10.12 上测试)和其他操作系统之间的 sed 差异:

OS=`uname`
# $(replace_in_file pattern file)
function replace_in_file() 
    if [ "$OS" = 'Darwin' ]; then
        # for MacOS
        sed -i '' -e "$1" "$2"
    else
        # for Linux and Windows
        sed -i'' -e "$1" "$2"
    fi

用法:

$(replace_in_file 's,MASTER_HOST.*,MASTER_HOST='"$MASTER_IP"',' "./mysql/.env")

地点:

, 是一个分隔符

's,MASTER_HOST.*,MASTER_HOST='"$MASTER_IP"',' 是模式

"./mysql/.env" 是文件路径

【讨论】:

【参考方案12】:

这是 bash 脚本中的一个选项:

#!/bin/bash

GO_OS=$GO_OS:-"linux"

function detect_os 
    # Detect the OS name
    case "$(uname -s)" in
      Darwin)
        host_os=darwin
        ;;
      Linux)
        host_os=linux
        ;;
      *)
        echo "Unsupported host OS. Must be Linux or Mac OS X." >&2
        exit 1
        ;;
    esac

   GO_OS="$host_os"


detect_os

if [ "$GO_OS" == "darwin" ]; then
    sed -i '' -e ...
else
    sed -i -e ...
fi

【讨论】:

您能以某种方式仅以gsed 存在为条件并将其用作变量吗?? @perrohunter 有点晚了(只有 1 年半?),但是有可能。例如。 if type gsed &> /dev/null; then; echo "use gsed"; else; echo "use sed"; fi; @Sinetris 迟到总比不到好哈哈哈【参考方案13】:

我不使用sed 调用sed,而是使用./bin/sed

这是我 ~/project/bin/sed 中的包装脚本

#!/bin/bash

if [[ "$OSTYPE" == "darwin"* ]]; then
  exec "gsed" "$@"
else
  exec "sed" "$@"
fi

不要忘记chmod 755 包装脚本。

【讨论】:

假设您事先在 Mac 上完成了brew install gnu-sed

以上是关于带有 -i 选项的 sed 命令在 Mac 上失败,但在 Linux 上有效的主要内容,如果未能解决你的问题,请参考以下文章

sed命令在mac和linux下的区别

在mac上使用sed删除文件中第一次出现的字符串[重复]

Mac上的Linux玩法

linux sed 命令

linux sed命令就是这么简单

sed命令