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

Shell 如何使用sed解析和修改查找结果?,shell,command-line,sed,find,ksh,Shell,Command Line,Sed,Find,Ksh,我偶然发现了以下问题:在一行上,我想更改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 {} ` e

我偶然发现了以下问题:在一行上,我想更改
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\/\2_\4/' ` \;
但它说它想复制到同一个文件中——我的替换没有进行;出现以下错误:

cp:“./jcho/category1/001.data”和“./jcho/category1/001.data”是 同一文件

因此,我尝试了一种带有子shell的方法,并将结果提供给它。 这(有点)奏效了

==>

如果我能在
sed
模式中包含
/
,我的解决方案就在眼前。。。 但我得到:

find jcho -name *.data -exec sh -c \
'f="${0}"; d=$(echo ${f} | sed -re 's/(jcho\/)(category[0-9]*)(\/)(.*data)/jcho2\/\2_\4/' ); cp ${f} ${d} ' {} \;
-ksh:语法错误:`('意外

…我试着用
\
逃离
/
,用
\
逃离
\
…没有更好

等同于

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

谢谢您的帮助!

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

find jcho -name '*.data' | 
while read -r f; do cp "$f" "$(echo "$f" | sed 's~\([^)]*\)/\([^()]*\)$~\1_\2~')"; done
通过
查找
输出,它将给出:

jcho/category1_001.data
jcho/category2_002.data
需要转义,以便以后引用

必须使用正则表达式模式中的[/]或替换零件中的\//进行转义

您可以尝试下面的命令行:

$ find jcho -name \*.data | sed -n '
{
h
s/^\(.*\)[/]\(category[^/]*\)[/]\(.*[.]data\)$/\12\/\2_\3/
H
x
s/\n/ /
p
}' | xargs -L 1 cp
这适用于提供的示例,但如果路径名或文件名包含特殊字符:\n“”空格,则会出现问题

以下是关于这些主题的著名页面:

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

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

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

pattern buffer = jcho/category1/001.data
hold buffer = jcho/category1/001.data
2-h用模式缓冲区的副本覆盖保持缓冲区的内容

pattern buffer = jcho/category1/001.data
hold buffer = jcho/category1/001.data
3-第一个s更改模式缓冲区的内容

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打印当前图案缓冲区

pattern buffer = jcho/category1/001.data
hold buffer = jcho/category1/001.data

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

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

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

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

这与使用正则表达式模式和文件名替换使用
cp
[link]()检查那里的答案。不,它更具体。我尝试了不同的回答,但无法。我的问题向SED提出解决方案谢谢,但我仍然得到
-ksh:语法错误:
)“意外的`为什么
ksh
?您将问题标记为
bash
,并且应该只使用
bash
。很抱歉,我重新标记了它。
sed
bash
ksh
中保持不变,所以这是另外一个问题。您能告诉我您正在尝试的是什么命令吗?谢谢Jay,但我正在尝试使用
sed
带下标的方式-没有循环。@J.Chomel答案已经更新。让我知道你的评论。这对我很有效,谢谢。我想我在sed和理解
h
h
x
p
方面会有困难。@J.Chomel答案已经更新,请参阅底部的新章节。字母已被描述。我尝试了这种方法,但
dirname
不是正确的目的地。这是我要复制到的另一个路径:
jcho2