我正在尝试在工作中放弃 AppleScripts 的工作流程,并创建一些可以在后台运行的更简单的东西。对于此任务,我每晚都会收到 35-40 个文件(7 或 8 个文件的 5 个不同质量的版本),并且我需要提取文件名的一部分。
例如,这些文件中的一批(缩写)可能如下所示:
每个文件有 5 个版本
ab_12_345_01_dest_xxxxxxxxxx_640x360_1000.jpg
ab_12_345_01_dest_xxxxxxxxxx_768x432_3000.jpg
ab_12_345_01_dest_xxxxxxxxxx_960x540_5000.jpg
ab_12_345_01_dest_xxxxxxxxxx_1280x720_7000.jpg
ab_12_345_01_dest_xxxxxxxxxx_1920x1080_9000.jpg
这些文件都是这样命名的(使用最高版本,我稍后会解释为什么):
ab_12_345_01_dest_xxxxxxxxxx_1920x1080_9000.jpg
ab_12_345_02_dest_yyyyyyyyyy_1920x1080_9000.jpg
ab_12_345_03_dest_zzzzzzzzzz_1920x1080_9000.jpg
ab_12_345_part1_aaaaaaaaaa_1920x1080_9000.jpg
ab_12_345_part2_bbbbbbbbbb_1920x1080_9000.jpg
ab_12_345_part3_special_cccccccccc_1920x1080_9000.jpg
ab_12_345_part4_dddddddddd_1920x1080_9000.jpg
ab_12_345_04_dest_special_eeeeeeeeee_1920x1080_9000.jpg
所以我的目标是使用9000
文件名的一部分来 grep 仅每个文件的最高版本(复制时间最长,所以如果它在那里,其余文件也在那里),然后提取所有内容到倒数第二_
。到目前为止,我已经能够完成第一部分,但无法完成第二部分。
当我这样做时,我只能得到最高版本的列表:
$ ls | grep 9000
ab_12_345_01_dest_xxxxxxxxxx_1920x1080_9000.jpg
ab_12_345_02_dest_yyyyyyyyyy_1920x1080_9000.jpg
ab_12_345_03_dest_zzzzzzzzzz_1920x1080_9000.jpg
ab_12_345_part1_aaaaaaaaaa_1920x1080_9000.jpg
ab_12_345_part2_bbbbbbbbbb_1920x1080_9000.jpg
ab_12_345_part3_special_cccccccccc_1920x1080_9000.jpg
ab_12_345_part4_dddddddddd_1920x1080_9000.jpg
ab_12_345_04_dest_special_eeeeeeeeee_1920x1080_9000.jpg
然后我尝试ls | grep 9000 | perl -pe '/^.+(?=_.+_.+)/mg
认为我会得到以下内容(基于每个在线 RegEx 测试人员,特别是我能找到的 Perl RegEx 测试人员所说的可行):
$ ls | grep 9000 | perl -pe '/^.+(?=_.+_.+)/mg`
ab_12_345_01_dest_xxxxxxxxxx
ab_12_345_02_dest_yyyyyyyyyy
ab_12_345_03_dest_zzzzzzzzzz
ab_12_345_part1_aaaaaaaaaa
ab_12_345_part2_bbbbbbbbbb
ab_12_345_part3_special_cccccccccc
ab_12_345_part4_dddddddddd
ab_12_345_04_dest_special_eeeeeeeeee
然而,我得到了同样的结果,就好像我从未通过管道传输到 Perl 一样。我最初尝试使用 awk 来实现这一点,但是我输入的命令变得相当长,我认为 RegEx 可能是可行的方法。但是,我需要正向前瞻,以便让它在倒数第二个位置停止匹配_
(而不是_
从字符串的开头开始计数),并且__
当我设置 .awk 时, awk 保留了最后一个{$NL=$(NL-1)=""; print $0}
。
答案1
使用你的perl
命令,你总是打印该行,因为你有-p
选择。匹配部分不做任何事情。
您想要-n
并打印匹配的部分:
ls -1 *9000.jpg \
| perl -lne 'print $1 if /^(.+)(?=_.+_.+)/'
由于文件名可能有换行符,您应该修改它以读取零分隔的文件名,但在您的情况下可能不需要:
printf '%s\0' *9000.jpg \
| perl -lne 'INIT{ $/ = "\0"}; print $1 if /^(.+)(?=_.+_.+)/'
或者,在 for 循环中读取文件名,然后您可以仅使用 shell 参数扩展:
for f in *9000.jpg; do printf '%s\n' "${f%_*_*}"; done
这可能更适合您的任务。 (=>“不要在文件名上使用基于行的文本编辑工具。”@Kusalananda)
答案2
无需通过管道从 到ls
来grep
过滤您的文件列表,您只需
ls *9000.jpg
此外,grep
它还会挑选出名称中其他位置恰好有 9000 的任何文件。
您的正则表达式没有问题,只有perl
.使用grep
你就会得到你想要的
ls *9000.jpg | grep -Po "^.+(?=_.+_.+)"
另一种方法可以是
find . -iname "*9000.jpg" -exec sh -c 'basename ${1%_*_*}' sh {} \;
其find
作用与ls
扩展会删除从倒数第二个字符到字符串末尾的字符,并${1%_*_*}
删除结果中包含的文件路径。_
basename
find
构造
-exec sh -c `blah blah` sh {} \;
非常值得学习和使用find
,@Kusalananda 有一篇很好的文章这里
-exec
只是告诉find
对其输出执行“blah blah”,\;
意味着对每个结果分别执行“blah blah”,这sh -c 'put some script in here'
就是您想要对结果执行的操作,最后sh {}
将输出传递find
回中定义的脚本sh -c