目前我正在使用cron
中的命令将*.data
的副本从源路径复制到目标路径:
find /source_path -name *.data -exec cp {} /target_path \;
源结构是:
/source_path/category1/001.data
/source_path/category1/002.data
/source_path/category2/003.data
/source_path/category3/004.data
/source_path/categorya/005.data
/source_path/categoryb/006.data
在上述cron
命令之后,目标将包含:
/target_path/001.data
/target_path/002.data
/target_path/003.data
/target_path/004.data
/target_path/005.data
/target_path/006.data
我需要一个单行解决方案来替换我当前的cron命令,以便在执行后,目标将包含:
/target_path/category1_001.data
/target_path/category1_002.data
/target_path/category2_003.data
/target_path/category3_004.data
/target_path/categorya_005.data
/target_path/categoryb_006.data
将子目录名称附加为目标文件名的前缀。
感谢。
答案 0 :(得分:2)
选中此命令仅打印字符串:
$ find /source_path -name \*.data | while read -r filename; do printf "print version: cp %s %s\n" "${filename}" "$(printf "%s\n" "${filename}" | sed "s/^.*[/]\(category[^/]*\)[/]\(.*[.]data\)$/\/target_path\/\1_\2/")"; done
find 命令打印找到的文件名,每行一个。
读取-r文件名读取一行文本并将其存储到 filename 变量中。
找到... |同时读取-r filename ,将每行一个文件名列表写入管道。一次只能读取一个文件名。对于读取的每个文件名,执行 while 块中的命令。
sed 命令将路径名 /source_path/category1/001.data 更改为 /target_path/category1_001.data 。
我尽力在下面的行中解释 sed 的字符串参数,但是如果你介入这些主题,你应该阅读:
s / 是搜索和替换sed命令,后面跟着3个元素:" s / regex pattern / replacement / flag"
^ 一开始就意味着,开始行。
。表示任何一个字符。
* 表示之前指定的0或无限数量的字符。
[/]表示一个字符,字符 / 。 []用于转义 / ,否则会被解释为正则表达式,替换和标记之间的分隔符
全部 ^。* [/] ,表示以任何零个或多个字符开头的行。此起始序列必须以 / 结束。
[^ /]表示一个字符, ^ 在开始时表示不是列出的字符的一部分。因此,它表示除 / 之外的任何一个字符。
[abc]在[]之间,表示一个字符:要么是b要么是c。
正则表达式中遇到的第一个 \(。* \)可以在替换中 \ 1 引用。 正则表达式中遇到的第二个 \(。* \)可以通过替换中的 \ 2 引用。如果没有 \ 转义字符,(表示单个字符(
,并且无法引用内容。
完成后,使用 cp 来有效复制文件:
find /source_path -name \*.data | while read -r filename; do cp "${filename}" "$(printf "%s\n" "${filename}" | sed "s/^.*[/]\(category[^/]*\)[/]\(.*[.]data\)$/\/target_path\/\1_\2/")"; done