【问题标题】:How to search and replace with egrep and sed on macOS?如何在 macOS 上使用 grep 和 sed 进行搜索和替换?
【发布时间】:2019-06-27 02:11:30
【问题描述】:

我想匹配文件中的模式并替换它。

This command 与 egrep、xargs 和 sed 一起使用:

egrep -lRZ "hello" . | xargs -0 -l sed -i -e 's/hello/world/g'

问题:在 MacOS 上不起作用,因为 MacOS 的 xargs 不支持参数 -l。

xargs: illegal option -- l
usage: xargs [-0opt] [-E eofstr] [-I replstr [-R replacements]] [-J replstr]
             [-L number] [-n number [-x]] [-P maxprocs] [-s size]
             [utility [argument ...]]

这在 MacOS 上如何解决?

【问题讨论】:

  • 我无法相信您的命令(如此处所示)正在运行。您缺少一个管道 |sed 的一半 s/search/replace/ 表达式。
  • 没错。我修好了。
  • 如果您edit 向我们展示了该脚本的用途(即提供minimal reproducible example),那么我们可以帮助您合理地使用find 编写它以查找 文件,以及其他适当的标准 UNIX 工具。

标签: bash macos sed grep xargs


【解决方案1】:

在 GNU (Linux) 与 bsd (macOS) 实用程序之间,您实际上会遇到三个不兼容问题。

  • 您收到的错误消息是 bsd 的 xargs 不接受 -l 选项。但是-l 等价于-L,除了-L 需要一个参数来指定每次调用命令时传递的最大行数,而-l 默认为1,如果不是' t 指定。因此,您只需将-l 替换为-L1-L xargs 的 GNU 和 bsd 版本的理解方式相同,因此使用它可以在 Linux 和 macOS 之间移植。

    但在这种特殊情况下,还有另一个更简单的选择:sed 完全能够在每次调用时对多个文件进行操作,因此没有理由将其限制为每次调用一个。这甚至会稍微快一些,因为它不必花费太多时间来启动新流程。因此,请关闭 -l

  • egrep 的 GNU 和 bsd 版本(以及grep 系列中的其他版本)都采用了-Z 选项,但它们使用它来表示完全不同的东西。使用 GNU,egrep -Z 在每个文件名之后打印零字节(ASCII NUL 字符)(与 xargs -0 期望的匹配)。但是对于 bsd,egrep -Z 等同于 zgrep -- 它将其输入文件视为 zip 存档,并在搜索其内容之前对其进行扩展。

    幸运的是,两个版本都支持 --null 调用零字节分隔符,因此您可以在两个平台上轻松使用它。

  • GNU 和 bsd 版本都将-i<suffix> 理解为“就地编辑,但制作备份副本,并使用指定的文件名后缀备份原始文件”。对于他们两个,如果后缀是零长度,它不会保留备份。不幸的是,您指定零长度后缀的方式是不同的,并且(据我所知)不可调和不兼容。具体来说,GNU 要求后缀直接附加到-i(例如-i.bkp),因此仅指定-i 本身就足以指定就地无备份模式。但是 bsd 允许将后缀作为单独的参数传递(例如 -i .bkp),因此如果您只指定 -i 本身,它将使用下一个参数作为后缀(例如 sed -i -e 's/hello/world/g' 将使用 " -e”作为后缀)。要指定就地无备份模式,您需要在 -i 后面加上一个明确的空参数(例如 sed -i '' -e 's/hello/world/g')。但是如果你用 GNU 的 sed 这样做,它会尝试将空参数作为它的脚本执行,这将失败。

综上所述,这是您的命令的 macOS 版本:

egrep -lR --null "hello" . | xargs -0 sed -i '' -e 's/hello/world/g'

...几乎可以在 Linux 上运行——唯一的区别是您需要将 '' 参数删除到 sed。如果您想要在 Linux 和 macOS 之间完全可移植的东西,您需要指定一个备份后缀(并将其直接附加到 -i 选项,如 -i.bkp)。

【讨论】:

  • * 注意:要进行试运行,首先从 sed 命令中删除 -i ''
【解决方案2】:

最好避免使用递归搜索文件的 grep 选项——它们只会弄乱你的 grep 参数并使你的脚本不可移植。已经有一个非常好的工具,旨在查找具有非常明显名称的文件。

您是否只是想在所有文件中将hello 替换为world?如果是这样,那只是

find . -type f |
while IFS= read -r file; do
    sed 's/hello/world/g' "$file" > "tmp$$" &&
    mv "tmp$$" "$file"
done

除非您的文件名包含换行符,否则这将在任何 UNIX 机器上的任何 shell 中工作。如果您不想更改不包含 hello 的文件的时间戳等,一种方法是:

find . -type f -exec grep -q 'hello' {} \; -print |
while IFS= read -r file; do
    sed 's/hello/world/g' "$file" > "tmp$$" &&
    mv "tmp$$" "$file"
done

【讨论】:

  • * 注意,要先进行试运行,请使用 echo "tmp$$" "$file" 而不是 mv "tmp$$" "$file"
  • 谢谢。我以后不仅要匹配原始文本,还要匹配正则表达式,所以是sed -e,对吧?
  • sed 仅对正则表达式进行操作,-e 对于 POSIX 来说是可选的,只需一个脚本即可解释。如果原始文本是指文字字符串-您根本不会使用 sed ,那么您将使用 awk ,因为 awk 具有文字字符串的功能。请参阅stackoverflow.com/q/29613304/1745001 了解您必须跳过的圈子才能让 sed 像在文字字符串上操作一样。
猜你喜欢
  • 2010-11-13
  • 2011-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-13
  • 2018-08-21
  • 2022-01-24
相关资源
最近更新 更多