我想使用 Bash、Perl 和 Regex 从文本文件中提取变量。
该文件如下所示(并且已读入变量 $str):
Filename: XXXXX
Type: XXX
Size: XXXX
Unimportant thing: XXXX
Filename: YYYYY
Type: YYY
Size: YYYY
Unimportant thing: YYYY
我需要每个块的文件名、类型和大小。数组是最好的,但包含由给定字符分隔的这些变量的字符串也是可以接受的。
然而,有时某些字段(例如大小或类型)会丢失。我想省略这些记录,所以我想我需要一个可以通过多行匹配的正则表达式。
我尝试了以下方法:
perl -pe 's/Filename: ([^\n]*)\nType: ([^\n]*)\nSize: ([^\n]*)\n/\1\t\2\t\3\n/' <<< $str
但这没有修改就打印出了原始文本。
然后我在没有 p 命令行参数的情况下尝试了它(我希望通过这种方式处理整个文件而不是迭代行):
perl -e 's/Filename: ([^\n]*)\nType: ([^\n]*)\nSize: ([^\n]*)\n/\1\t\2\t\3\n/' <<< $str
这个没有打印任何东西(空结果)。
然后我尝试在正则表达式前面添加 print ,因为我认为删除 -p 可能会导致 Perl 不知道我想要打印结果:
perl -e 'print s/Filename: ([^\n]*)\nType: ([^\n]*)\nSize: ([^\n]*)\n/\1\t\2\t\3\n/' <<< $str
仍然没有成功(空结果)。
我缺少什么?
更新:
我希望将其作为一行 perl 命令。
答案1
我的 Perl 知识很薄弱,但由于没有其他人提供 Perl 答案,所以我会尝试一下。
将数据作为文件传递,它将打印制表符分隔的行,每行三个值:
perl -e 'while (<>) { $s .= $_; } chomp $s; @arr = split(/\n{2,}/, $s); foreach my $a(@arr) { $a =~ s/Filename: ([^\n]*)\nType: ([^\n]*)\nSize: ([^\n]*)\n.*/$1\t$2\t$3\n/ || next; print "$a"; } ' infile
结果:
XXXXX XXX XXXX
YYYYY YYY YYYY
这有点暴力,但可以通过将输入分成段落/块,然后将多行正则表达式应用于每个段落/块来工作。
细节...
while (<>) { $s .= $_; }
- 将输入变成单个字符串。chomp $s
- 从字符串中删除尾随换行符。@arr = split(/\n{2,}/, $s)
- 在连续的换行符上分割字符串。这将其分成段落/块。将块存储在数组中。foreach my $a(@arr)
- 循环每个数组元素(块)。接下来的两行代码将应用于每个块。$a =~ s/Filename: ([^\n]*)\nType: ([^\n]*)\nSize: ([^\n]*)\n.*/$1\t$2\t$3\n/ || next
- 从三个感兴趣的字段中提取值。如果没有发生替换(意味着正则表达式不匹配,因为例如缺少值),则跳过此块并移至下一个。print "$a"
- 打印替换结果:用制表符分隔的三个值。
再说一次,我不太使用 Perl,所以可能有比这更优雅的解决方案。
答案2
不是 perl 方面的大专家,但使用sed
它会看起来像这样:
sed -n '/^$/d;/^Filename/,/^Unimportant/{:a;/Unimportant/!{N;ba};s/Filename: \([^\n]*\)\nType: \([^\n]*\)\nSize: \([^\n]*\)\n.*/\1\t\2\t\3/p};'
在哪里:
/^$/d
-- 将删除所有空行/^Filename/,/^Unimportant/
将分别匹配从 Filename 到 Unimportant 的每个块。我假设您在每个块中都有不重要的记录。:a;/Unimportant/!{N;ba};
将把整个块连接到一个缓冲区中。需要,因为sed
无法使用多行正则表达式或以其他方式一次处理多行s/Filename: \([^\n]*\)\nType: \([^\n]*\)\nSize: \([^\n]*\)\n.*/\1\t\2\t\3/p};
将替换为您需要的格式(基于您的 perl 正则表达式)