在嵌套 files/directories 中使用正则表达式在一行中搜索多个字符串并输出匹配结果

Search multiple strings in one line using regex in nested files/directories and output matched results

例如有文件和目录:

/tmp/temp_dir/subdir_001/file_001.txt
/tmp/temp_dir/subdir_001/file_002.txt
/tmp/temp_dir/subdir_002/file_003.txt
/tmp/temp_dir/subdir_003/file_004.txt

并且这些内容包含可以通过正则表达式找到的特定行的各种内容。例如,这里是文件 file_001.txt 的内容:

abc cba
little boy writes -54321_12345 and goes to street 987
bca acb
little boy writes -12345_54321 and jumps to street 789
cab bac

我感兴趣的是那些以 little boy writes 开头的行。 我正在使用此正则表达式模式来查找我想保存为输出的重要数据: little boy writes (\-\d+\_\d+).*street (\d+)

如何递归搜索并只输出匹配的字符串?所以在输出文件中我只会有这个:

54321_12345 987
12345_54321 789

findsed 的组合应该可以解决问题:

find /tmp/temp_dir/ -type f -exec sed -En 's/little boy writes -([0-9]+_[0-9]+).*street ([0-9]+)/ /p' {} + > output

细分:

  • find /tmp/temp_dir/ -type f : 我们从根文件夹递归地找到每个文件
  • -exec sed '... ' {} + 运行s 对找到的每个文件执行命令(这里 {} 表示 find 命令检索到的项目, + 表示命令再次执行一次最终结果,as explained here)
  • sed -En 's/little boy writes -([0-9]+_[0-9]+).*street ([0-9]+)/ /p' :我们 运行 您在问题中描述的模式,使用 sed\d 不是有效的 sed 字符 class,我们使用 [0-9] 代替)
  • > output 我们将此命令的输出重定向到名为 output
  • 的文件

您可以将 grepsed 结合使用:

$ grep '^little boy writes' /tmp/temp_dir/subdir_*/file_*.txt | sed -re 's/^.* -([0-9]+_[0-9]+).*street ([0-9]+)/ /' > output.txt

可以 只用递归 grep 得到行,有或没有文件名。

grep -r  '^little boy writes' *  # lists source filenames
grep -hr '^little boy writes' *  # does not

不过,这会报告整行。 Perl 模式匹配 (-P) 与 -o 可能会检测到正确的行,并且只能检测到 return 你想要的位,但是对于大多数人来说,这种模式很难理解和维护,所以它是可能值得第二个过程-

grep -hr '^little boy writes' /tmp/temp_dir/subdir_[0-9][0-9][0-9]/file_[0-9][0-9][0-9].txt |
  sed -E 's/[^0-9_]*([0-9_]+)/ /g'

或者如果你真的想在最后避免 space,

grep -hr '^little boy writes' /tmp/temp_dir/subdir_[0-9][0-9][0-9]/file_[0-9][0-9][0-9].txt |
  's/^[^0-9_]*([0-9_]+)[^0-9_]*([0-9_]+$)/ /'

但是,如果您确切知道这些文件的位置足以进行这样的 globbing,那么您所需要的只是 sed。

sed -En '/^little boy writes/{ s/^[^0-9_]*([0-9_]+)[^0-9_]*([0-9_]+$)/ /g; p; }' /tmp/temp_dir/subdir_[0-9][0-9][0-9]/file_[0-9][0-9][0-9].txt

如果你不这样做,grep and/or sed 可能会处理大量你可以避免的数据......也许你的目录结构不完全是这样持续的。在这种情况下,shopt 会有所帮助。

shopt -s globstar # let's ** stand for variable depth of subdirectories
sed -En '/^little boy writes/{ s/^[^0-9_]*([0-9_]+)[^0-9_]*([0-9_]+$)/ /g; p; }' **/file_[0-9][0-9][0-9].txt

这应该更有效(也更快)。它会让 OS 挑选匹配的文件并只将那些文件交给 sed 进行扫描。

这也只使用 sed 的一个实例,而不是使用 find 或需要 xargs.

为每个文件生成一个实例

祝你好运。