如何在 macOS 上使用 egrep 和 sed 进行搜索和替换?
How to search and replace with egrep and sed on macOS?
我想匹配文件中的模式并替换它。
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 上如何解决?
GNU (Linux) 与 bsd (macOS) 实用程序之间实际上存在三个不兼容问题 运行。
您收到的错误消息是 bsd 的 xargs
不接受 -l
选项。但是 -l
等同于 -L
除了 -L
需要 一个参数指定每次调用命令传递的最大行数,而 -l
如果未指定,则默认为 1。因此,您可以将 -l
替换为 -L1
。 -L
is GNU 和 bsd 版本的 xargs
以相同的方式理解,因此使用它可以在 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
本身就足以指定 in-place-without-backup 模式。但是 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 上工作——唯一的区别是您需要删除 [=21] 的 ''
参数=].如果你想要在 Linux 和 macOS 之间完全可移植的东西,你需要指定一个备份后缀(并将其直接附加到 -i
选项,如 -i.bkp
)。
最好避免递归搜索文件的 grep 选项 - 它们只会弄乱您的 grep args 并使您的脚本不可移植。已经有一个非常好的工具可以用来找到 名字很明显的文件。
您是否只是想将所有文件中的 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
我想匹配文件中的模式并替换它。
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 上如何解决?
GNU (Linux) 与 bsd (macOS) 实用程序之间实际上存在三个不兼容问题 运行。
您收到的错误消息是 bsd 的
xargs
不接受-l
选项。但是-l
等同于-L
除了-L
需要 一个参数指定每次调用命令传递的最大行数,而-l
如果未指定,则默认为 1。因此,您可以将-l
替换为-L1
。-L
is GNU 和 bsd 版本的xargs
以相同的方式理解,因此使用它可以在 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
本身就足以指定 in-place-without-backup 模式。但是 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 上工作——唯一的区别是您需要删除 [=21] 的 ''
参数=].如果你想要在 Linux 和 macOS 之间完全可移植的东西,你需要指定一个备份后缀(并将其直接附加到 -i
选项,如 -i.bkp
)。
最好避免递归搜索文件的 grep 选项 - 它们只会弄乱您的 grep args 并使您的脚本不可移植。已经有一个非常好的工具可以用来找到 名字很明显的文件。
您是否只是想将所有文件中的 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