如何使用 sed 解析和修改查找结果?

How to use sed to parse and modify the result of a find?

我偶然发现了以下问题:在 单行 上,我想更改 find 命令的结果以执行特定的 cp取决于我找到的文件,目标名称取决于 find.

返回的文件

这是我的文件,

$  find jcho -name *.data
jcho/category1/001.data
jcho/category2/002.data

并且必须将它们复制到

jcho2/category1_001.data
jcho2/category2_002.data

我试过了,

$ find . -name *.data \
-exec cp  {} ` echo {} | sed -re 's/(jcho\/)(category[0-9]*)(\/)(.*data)/jcho2\/_/' ` \;

但是它说它想复制到同一个文件——我的替换没有进行;出现以下错误:

cp: './jcho/category1/001.data' and './jcho/category1/001.data' are the same file

所以我尝试了一些带有子外壳的东西,我将查找的结果提供给它。 这(有点)有效。

find jcho -name *.data -exec sh -c \ 
'f="[=13=]"; d=$(echo ${f} | sed -re 's/0/2/' ); cp ${f} ${d} ' {} \;

==>

find jcho -name 2*.data
jcho/category1/201.data
jcho/category2/202.data

如果我可以在 sed 模式中包含 /,我的解决方案就在眼前...

但我得到:

find jcho -name *.data -exec sh -c \
'f="[=15=]"; d=$(echo ${f} | sed -re 's/(jcho\/)(category[0-9]*)(\/)(.*data)/jcho2\/_/' ); cp ${f} ${d} ' {} \;

-ksh: syntax error: `(' unexpected

...我尝试使用 \ 转义 /,使用 \... 不是更好。

同上

find jcho -name *.data -exec sh -c \
'f="[=16=]"; d=$(echo ${f} | sed -re 's~\([^)]*\)/\([^()]*\)$~_~' ); cp ${f} ${d} ' {} \;

感谢您的帮助!

您可以将此 sed 与替代分隔符一起使用 ~:

find jcho -name '*.data' | 
while read -r f; do cp "$f" "$(echo "$f" | sed 's~\([^)]*\)/\([^()]*\)$~_~')"; done

您的 find 输出将给出:

jcho/category1_001.data
jcho/category2_002.data

需要对( and )进行转义,以便后面引用

/ 必须使用正则表达式模式中的 [/]\/[=65 进行转义=]在替换部分。

您可以尝试以下命令行:

$ find jcho -name \*.data | sed -n '
{
h
s/^\(.*\)[/]\(category[^/]*\)[/]\(.*[.]data\)$/\/_/
H
x
s/\n/ /
p
}' | xargs -L 1 cp

这适用于提供的示例,但如果路径名或文件名包含特殊字符,则会出现问题:\n " ' space.

有关于这些主题的著名页面可供阅读:

让我们看看 sed 将如何处理从管道读取的每个文件名。

0- -n 选项指示 sed 不打印行,除非使用 p

1- jcho/category1/001.从管道中读取数据并将其存储在模式缓冲区中。

pattern buffer = jcho/category1/001.data

2- h 用模式缓冲区的副本覆盖保持缓冲区的内容。

hold buffer = jcho/category1/001.data

3-第一个s改变了pattern buffer的内容

pattern buffer = jcho2/category1_001.data

4- H 将模式缓冲区添加到保持缓冲区

hold buffer = jcho/category1/001.data
jcho2/category1_001.data

5- x 交换缓冲区内容

pattern buffer = jcho/category1/001.data
jcho2/category1_001.data

6- 最后一个 s 命令将模式缓冲区中的 '\n' 替换为 ' '

pattern buffer = jcho/category1/001.data jcho2/category1_001.data

7- p 打印当前模式缓冲区。

8- 块结束:return 到 1 并使用找到的下一个文件名。

可能有比这更简单的方法,但是使用 bash basename/dirname 组合你可以达到同样的效果

for f in $(find ...); do cp $f $(echo $(dirname $f)"_"$(basename $f)); done

无需使用正则表达式匹配进行字符串操作。