如何使用 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
无需使用正则表达式匹配进行字符串操作。
我偶然发现了以下问题:在 单行 上,我想更改 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
无需使用正则表达式匹配进行字符串操作。