【问题标题】:How to use sed to parse and modify the result of a find?如何使用 sed 解析和修改 find 的结果?
【发布时间】:2016-07-24 03:11:48
【问题描述】:

我偶然发现了以下问题:在一行上,我想根据我找到的文件更改我的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的东西,我给出了查找的结果。 这奏效了(有点)。

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

==>

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

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

但我明白了:

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} ' {} \;

感谢您的帮助!

【问题讨论】:

  • 这与find -name 使用正则表达式模式和使用cp [链接] (stackoverflow.com/questions/36396401/…) 替换文件名的问题相同。在那里查看答案。
  • 不,它更具体。我尝试以不同的方式回答它,但不能。我的问题是用 sed 提出解决方案

标签: shell command-line sed find ksh


【解决方案1】:

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

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

使用您的find 输出,它将给出:

jcho/category1_001.data
jcho/category2_002.data

【讨论】:

  • 谢谢,但我还是得到了-ksh: syntax error: )' 意外`
  • 为什么是ksh?您将问题标记为bash,并且应该只使用bash
  • 对此感到抱歉。我重新标记了它。
  • sedbashksh 中保持不变,所以这是另一回事。你能告诉我你正在尝试的完整命令是什么吗?
【解决方案2】:

() 需要转义以便以后引用。

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

你可以试试下面的命令行:

$ 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.data 从管道中读取并存储在模式缓冲区中。

pattern buffer = jcho/category1/001.data

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

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 打印当前模式缓冲区。

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

【讨论】:

  • 感谢 Jay,但我正在尝试使用带有下标的 sed 方式 - 没有循环。
  • @J.Chomel 答案已更新。让我知道你的意见。
  • 这对我有用,谢谢。我想我很难用 sed 和理解 hHxpyou 在那里使用。
  • @J.Chomel 答案已更新,请参阅底部的新教派。描述了这些字母。
【解决方案3】:

可能有比这更简单的方法,但是使用 bash 基本名称/目录名称组合可以实现相同的效果

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

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

【讨论】:

  • 我试过这种方式,但dirname 不是正确的目的地。这是我要复制到的不同路径:jcho2
猜你喜欢
  • 2018-07-15
  • 2011-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-02
  • 1970-01-01
  • 2014-05-14
  • 1970-01-01
相关资源
最近更新 更多