我有一个小脚本,它列出了目录和所有子目录中所有文件的路径,并使用Perl中的regex解析列表中的每个路径。
#!/bin/sh
find * -type f | while read j; do
echo $j | perl -n -e '/\/(\d{2})\/(\d{2})\/(\d+).*-([a-zA-Z]+)(?:_(\d{1}))?/ && print "\"0\";\"$1$2$3\";\"$4\";\"$5\";$fl\""' >> bss.csv
echo | readlink -f -n "$j" >>bss.csv
echo \">>bss.csv
done
输出:
"0";"13957";"4121113";"2";"/home/root/dir1/bss/164146/13/95/7___/000240216___Abc-4121113_2.jpg"
我正在使用GNU coreutils中的readlink
:-n
在结尾处抑制换行符,-f
通过递归跟踪路径上的符号链接来执行规范化。
问题是,当输入字符串没有通过正则表达式时,我只有文件路径行。
如何添加条件以检查正则表达式是否通过 - 显示路径,否则 - 否。 我用各种组合打破了我的大脑,但没有找到任何正常工作。
答案 0 :(得分:1)
在Perl中,使用if (/…/) {…} else {…}
代替/…/ && …
。因此,如果匹配成功,您可以执行打印,否则可以执行其他一些代码。
如果这不是问题而你只想摆脱readlink
输出和结束引用,你可以使用反引号从Perl调用readlink
。
我将所有内容都转换为单个Perl程序,使用File::Find
代替find
命令,假设$fl
在print
结束时Perl是一个遗留(忽略它)并使用Cwd::realpath()
从GNU coreutils中查找文件的规范路径而不是readlink -f
。如果您仍想使用readlink -f
,请随时将Cwd::realpath($_)
更改为`readlink -f '$_'`
(包括反引号!),但它对包含单引号的文件名不起作用。
您应该将此脚本称为./script-name starting-directory > bss.csv
。如果将它放在您正在检查的目录中,输出也会包含它,以及bss.csv
。
#!/usr/bin/perl
# Usage: ./$0 [<starting-directory>...]
use strict;
use warnings;
use File::Find;
use Cwd;
no warnings 'File::Find';
sub handleFile() {
return if not -f;
if ($File::Find::name =~ /\/(\d{2})\/(\d{2})\/(\d+).*-([a-zA-Z]+)(?:_(\d{1}))?/) {
local $, = ';', $\ = "\n";
print map "\"$_\"", 0, $1.$2.$3, $4, $5, Cwd::realpath($_);
} else {
print STDERR "File $File::Find::name did not match\n";
}
}
find(\&handleFile, @ARGV ? @ARGV : '.');
作为参考,我还附上原始程序的抛光版本。它正如我上面建议的那样从Perl调用readlink
并且真正使用了Perl的-n
选项,避免了while read
循环。
#!/bin/sh
find . -type f | perl -n -e 'm{/(\d{2})/(\d{2})/(\d+).*-([a-zA-Z]+)(?:_(\d{1}))?} && print qq{"0";"$1$2$3";"$4";"$5";"`readlink -f -n '\''$_'\''`"}' > bss.csv
答案 1 :(得分:1)
如果我理解你,你想要捕获文件名的以下部分:
/home/root/dir1/bss/164146/13/95/7___/000240216___Abc-4121113_2.jpg
~~ ~~ ~ ~~~ ~~~~~~~ ~
1 2 3 4 5 6
但是你的perl正则表达式并没有这样做。让我们分开以便更好地理解。
/\/(\d{2})\/(\d{2})\/(\d+).*-([a-zA-Z]+)(?:_(\d{1}))?/
切成碎片,这将是......
\/(\d{2})
- 斜线,然后是两位数字(捕获数字)\/(\d{2})
- 另一个斜杠和两位数字\/(\d)
- 还有一个斜杠和任意数字的数字.*-
- 直到输入字符串([a-zA-Z]+)
- 一个或多个字母字符(?:_(\d{1}))?
- 无意义的(我认为)构造匹配一个不会被捕获的可选单个数字(因为它在(?:...)
内)如果您单步执行文件名,您会发现此处没有任何内容可以处理最后一个数字字符串。
我会使用更简单的工具来做到这一点。塞德,例如:
[ghoti@pc ~]$ s="/home/root/dir1/bss/164146/13/95/7___/000240216___Abc-4121113_2.jpg"
[ghoti@pc ~]$ echo "$s" | sed -rne 's/.*/"&"/;h;s:.*/([0-9]{2})/([0-9]{2})/([0-9]+)[^[a-zA-Z]]*[^-]+-([0-9]+)(_([0-9]+))?.*:"0";"\1\2\3";"\4";"\6":;G;s/\n/;/;p'
"0";"13957";"4121113";"2";"/home/root/dir1/bss/164146/13/95/7___/000240216___Abc-4121113_2.jpg"
[ghoti@pc ~]$
我将分解sed脚本以便于阅读:
s/.*/"&"/;
- 在文件名周围加上引号。h;
- 将文件名存储在Sed的“保留”空间中,以备将来使用... s:
- 开始大替代......
.*/([0-9]{2})/([0-9]{2})/([0-9]+)[^[a-zA-Z]]*[^-]+-([0-9]+)(_([0-9]+))?.*
- 这是我们想要替换的模式。与您在Perl中所做的类似,显然,但使用ERE而不是PCRE。:"0";"\1\2\3";"\4";"\6":;
- 替换模式,其中\n
被替换为RE的括号内元素。请注意,替换字符串中会跳过\5
,因为该子表达式仅用于匹配。G;
- 将“保留”空间附加到模式空间s/\n/;/;
- 并删除它们之间的换行符。p
- 打印结果。请注意,此解决方案假设所有输入行都与您要查找的模式匹配。如果情况并非如此,那么您可能会得到不可预测的输出,并且应该在脚本中添加一些模式匹配。