如何在 Linux 上使用 exec 选项转义传递给 find 的命令

发布于 2024-10-10 09:58:06 字数 1546 浏览 9 评论 0原文

让我将我的问题分解为最简单的例子。

创建一个包含一行文本的测试文件。

[root@myserver ] /tmp> echo "test ReplaceMe DoNotReplaceMe" > /tmp/daj.txt

我们有一个现有的 find 命令,我们用它来替换所有与其匹配的文件中的文本(在本示例中,我简化了此命令,使其仅适用于一个文件,并删除了它的其他内容)做)。

问题是它在所有出现的地方都替换了“ReplaceMe”,而不是仅当它本身是一个词时才替换。

[root@myserver ] /tmp> find /tmp/daj.txt -exec sh -c 'f="{}"; sed -e 's/ReplaceMe/#DONE#/gi' "${f#.}" ' \;
test #DONE# DoNot#DONE#

我编写了一个新的 sed 命令,仅当“ReplaceMe”本身是一个单词时才替换它,但当它是另一个单词的子字符串时则不替换。该命令的输出是正确的。

[root@myserver ] /tmp> cat /tmp/daj.txt | sed -e 's/\(\W\)\(ReplaceMe\)\(\W\)/\1#DONE#\3/gi'    
test #DONE# DoNotReplaceMe

当我尝试将更新的 sed 命令合并到 find 命令中时,它崩溃了。看起来我遇到了转义问题,但我还没有通过添加额外的转义来解决它。

[root@myserver ] /tmp> find /tmp/daj.txt -exec sh -c 'f="{}"; sed -e 's/\(\W\)\(ReplaceMe\)\(\W\)/\1#DONE#\3/gi' "${f#.}" ' \;
sh: -c: line 0: syntax error near unexpected token `('
sh: -c: line 0: `f="/tmp/daj.txt"; sed -e s/(W)(ReplaceMe)(W)/1#DONE#3/gi "${f#.}" '

有没有办法转义我的 sed 命令,以便我可以通过 find 运行它,或者我是否必须寻找替代解决方案?

更新:我们正在运行的完整find命令打印出文件名和权限,然后将sed的输出通过管道传输到md5sum。这是运行并匹配多个文件的示例:

[root@myserver ] ~> find /tmp -regex '.*daj.*\.txt' -printf '%p %m ' -exec sh -c 'f="{}"; sed  -e 's/ReplaceMe/#DONE#/gi' "${f#.}" | md5sum' \;
/tmp/daj2.txt 644 d52bbd311552234b761bcae694c2055a  -
/tmp/daj.txt 644 d52bbd311552234b761bcae694c2055a  -

Let me break down my problem into the simplest example I can.

Create a test file containing one line of text.

[root@myserver ] /tmp> echo "test ReplaceMe DoNotReplaceMe" > /tmp/daj.txt

We have an existing find command that we use to substitute text in all the files that match it (in this example I've simplified this command to only work on one file, and stripped out the other stuff it does).

The problem is that it substitutes "ReplaceMe" everywhere it appears, instead of only when it is a word on its own.

[root@myserver ] /tmp> find /tmp/daj.txt -exec sh -c 'f="{}"; sed -e 's/ReplaceMe/#DONE#/gi' "${f#.}" ' \;
test #DONE# DoNot#DONE#

I've written a new sed command to only substitute "ReplaceMe" when it is a word on its own, but NOT when it is a substring of another word. The output from this command is correct.

[root@myserver ] /tmp> cat /tmp/daj.txt | sed -e 's/\(\W\)\(ReplaceMe\)\(\W\)/\1#DONE#\3/gi'    
test #DONE# DoNotReplaceMe

When I try to incorporate the updated sed command into the find command, it breaks. It looks like I am hitting an escaping problem, but I haven't managed to solve it by adding extra escaping.

[root@myserver ] /tmp> find /tmp/daj.txt -exec sh -c 'f="{}"; sed -e 's/\(\W\)\(ReplaceMe\)\(\W\)/\1#DONE#\3/gi' "${f#.}" ' \;
sh: -c: line 0: syntax error near unexpected token `('
sh: -c: line 0: `f="/tmp/daj.txt"; sed -e s/(W)(ReplaceMe)(W)/1#DONE#3/gi "${f#.}" '

Is there a way to escape my sed command so that I can run it via find, or do I have to look for an alternative solution?

Update: The full find command we are running prints out the filename and permissions, and then pipes the output of the sed to md5sum. Here's an example of it running and matching multiple files:

[root@myserver ] ~> find /tmp -regex '.*daj.*\.txt' -printf '%p %m ' -exec sh -c 'f="{}"; sed  -e 's/ReplaceMe/#DONE#/gi' "${f#.}" | md5sum' \;
/tmp/daj2.txt 644 d52bbd311552234b761bcae694c2055a  -
/tmp/daj.txt 644 d52bbd311552234b761bcae694c2055a  -

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

半城柳色半声笛 2024-10-17 09:58:06

您不应直接在 shell 中使用 {},而应将文件名作为 shell 参数传递。另外,如果您想限制全字匹配,请使用 \ 进行 sed

更新

find /tmp -regex '.*daj.*\.txt' -printf '%p %m ' -exec sh -c "sed  -e 's/\<ReplaceMe\>/#DONE#/gi' \$@ | md5sum" _ {} \;

输出

$ find . -regex '.*daj.*\.txt' -printf '%p %m ' -exec sh -c "sed  -e 's/\<ReplaceMe\>/#DONE#/gi' \$@ | md5sum" _ {} \;
./daj2.txt 664 ea324b4721ed037dbc2402ded4446005  -
./daj.txt 664 0bbb9104da99c1c1187a2a35e6ac0e9b  -

You should not be using {} directly in the shell, instead you should be passing the file names in as shell parameters. Also, if you want to limit to whole-word matches then use \<word\> for sed

Update

find /tmp -regex '.*daj.*\.txt' -printf '%p %m ' -exec sh -c "sed  -e 's/\<ReplaceMe\>/#DONE#/gi' \$@ | md5sum" _ {} \;

Output

$ find . -regex '.*daj.*\.txt' -printf '%p %m ' -exec sh -c "sed  -e 's/\<ReplaceMe\>/#DONE#/gi' \$@ | md5sum" _ {} \;
./daj2.txt 664 ea324b4721ed037dbc2402ded4446005  -
./daj.txt 664 0bbb9104da99c1c1187a2a35e6ac0e9b  -
沉鱼一梦 2024-10-17 09:58:06

这并没有回答您有关转义序列的问题,但确实解决了问题。我基本上会像这样使用 xargssed

$ find ~/tmp/data.txt | xargs sed -e 's/\<replaceme\>/1234/'
1234 in this sentance
donotreplaceme in this sentance
$ 

以及 data.txt 的内容:

replaceme in this sentance
donotreplaceme in this sentance

另外,如果您可能使用 -print0 的文件名中包含空格 参数告诉 find 将文件列表输出为以 null 结尾的字符串。否则 find 会将文件名中的空格解释为文件名的末尾。然后,当使用 xargs 时,您需要使用 -0 参数来告诉 xargs 输入是一个以 null 结尾的字符串列表。下面的例子:

find /somedir -print0 | xargs -0 command

This doesn't answer your question about the escape sequence but does solve the problem. I'd basically use xargs with sed like this:

$ find ~/tmp/data.txt | xargs sed -e 's/\<replaceme\>/1234/'
1234 in this sentance
donotreplaceme in this sentance
$ 

and the contents of data.txt:

replaceme in this sentance
donotreplaceme in this sentance

Also if you might have filenames with spaces in it using the -print0 parameter tells find to output the list of files as null terminated strings. Otherwise find will interpret the space in the filename as the end of the the filename. Then when using xargs you need to use the -0 parameter to tell xargs that the input is a list of null terminated strings. Example below:

find /somedir -print0 | xargs -0 command
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文