我想匹配文件中的模式并替换它。
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?
答案 0 :(得分:2)
在GNU(Linux)实用工具和bsd(macOS)实用工具之间实际上会遇到三种不兼容性。
您收到错误消息的原因是bsd的xargs
不接受-l
选项。但是-l
与-L
等效,除了-L
需要一个参数,该参数指定每次调用命令要传递的最大行数,而{{1} }如果未指定,则默认为1。因此,您可以将-l
替换为-l
。 -L1
被-L
的GNU版本和bsd版本理解相同,因此可以在Linux和macOS之间移植。
但是在这种特殊情况下,还有另一个更简单的选择:xargs
完全能够在每次调用时对多个文件进行操作,因此没有理由将其限制为每次调用一个文件。这甚至会稍微快一些,因为它不必花费太多时间来启动新流程。因此,只需关闭sed
。
-l
的GNU和bsd版本(以及egrep
系列中的其他版本)都使用选项grep
,但是它们用的含义完全不同。对于GNU,-Z
在每个文件名后打印零字节(ASCII NUL字符)(与egrep -Z
期望的匹配)。但是使用bsd时,xargs -0
等效于egrep -Z
-将其输入文件视为zip存档,并在搜索其内容之前对其进行扩展。
幸运的是,两个版本都了解zgrep
来调用零字节定界符,因此您可以在两个平台上都可移植地使用它。
GNU和bsd版本都将--null
理解为“就地编辑,但要进行备份,并使用指定的文件名后缀备份原始文件”。对于这两个文件,如果后缀为零长度,则不会保留备份。不幸的是,您指定零长度后缀的方式是不同的,并且(据我所知)是不可调和地不兼容的。具体来说,GNU要求后缀直接附加到-i<suffix>
(例如-i
),因此仅指定-i.bkp
本身就足以指定就地无备份模式。但是bsd允许将后缀作为单独的参数传递(例如-i
),因此,如果仅自己指定-i .bkp
,它将使用下一个参数作为后缀(例如{{ 1}}将使用“ -e”作为后缀)。要指定就地无备份模式,您需要在-i
之后加上一个明确的空参数(例如sed -i -e 's/hello/world/g'
)。但是,如果使用GNU的-i
来执行此操作,它将尝试执行空参数作为其脚本,这将失败。
所有这些,这是命令的macOS版本:
sed -i '' -e 's/hello/world/g'
...几乎可以在Linux上工作的
...唯一的区别是您需要删除sed
的{{1}}参数。如果要在Linux和macOS之间完全移植,则需要指定备份后缀(并将其直接附加到egrep -lR --null "hello" . | xargs -0 sed -i '' -e 's/hello/world/g'
选项,如''
所示)。
答案 1 :(得分:2)
最好避免使用grep递归搜索文件的选项-它们只会使grep arg杂乱无章,并使脚本不可移植。已经有一个非常好的工具,可以用来查找名称非常明显的文件。
您是否只是想在所有文件中将hello
替换为world
?如果是这样的话
find . -type f |
while IFS= read -r file; do
sed 's/hello/world/g' "$file" > "tmp$$" &&
mv "tmp$$" "$file"
done
除非您的文件名包含换行符,否则它将在任何UNIX盒的任何外壳程序中都有效。如果您不想更改不包含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