如何漂亮的 grep 输出以便在 Markdown 文件中搜索?

如何漂亮的 grep 输出以便在 Markdown 文件中搜索?

我有一个 Markdown 文件的知识库(又名 Zettelkasten)。我使用下一个命令进行搜索grep -irn 'search request' *.md。一切正常。

但我希望看到带有文件标题和找到的字符串的子标题的输出。

例子

文件.md

1 # Title
2
3 ## Subtitle
4
5 yada-yada

输出

> grep -irn 'yada' *.md

< file.md:5:Title:Subtitle:yada-yada

我可以在grep不多次搜索文件的情况下执行此操作吗?

类似物记起对于终端来说是理想的选择。

答案1

不,grep 不能这样做。但是,您可以使用 awk 或 perl 轻松编写自己的自定义搜索工具。例如

find ./ -name '*.md' -exec awk -v search="yada-yada" '
  /^# /  { title=$0;    sub(/^# /,"",title)     };
  /^## / { subtitle=$0; sub(/^## /,"",subtitle) };
  $0 ~ search { printf "%s:%i:%s:%s:%s\n", FILENAME,FNR,title,subtitle,$0 }' {} +

这是一个非常原始的示例,可以根据您的具体需求进行定制,或者在其他方面进行很大的改进。它可能应该封装在 shell 脚本中,这样您就可以拥有类似的东西,-v search="$1"而不是将其硬编码为"yada-yada".

这是一个稍微好一点的版本,用 Perl 编写。这个不需要find(它使用perl自己的文件::查找模块)并且可能更容易通过更好的选项处理进行扩展(例如,您可以支持搜索类似于 的多个目录find,或添加-i-v不区分大小写的选项或反转匹配,与 grep 和其他一些程序相同):

#!/usr/bin/perl

use strict;
use File::Find;

# Very primitive argument handling, should use Getopt::Long or
# one of the other Getopt::* modules
my $path   = shift;    # first arg is dir to search, e.g. './'
my $search = shift;    # second arg is regex to search for

find({ wanted => \&wanted, no_chdir => 1}, $path);

sub wanted {
  # This uses \z in the regex rather than just $ because
  # filenames can contain newlines.
  next unless (-f $File::Find::name && /\.md\z/s);
  
  # open the file and "grep" it.
  open(my $fh, "<", $File::Find::name) || warn "couldn't open $File::Find::name: $!\n";
  
  my $title    = '';
  my $subtitle = '';
  
  while(<$fh>) {
    chomp;
    
    if (/^# /) {
      ($title = $_) =~ s/^# //;
    
    } elsif (/^## /) { 
      ($subtitle = $_) =~ s/^## //;
    
    } elsif (/$search/) {
      printf "%s:%i:%s:%s:%s\n", $File::Find::name, $., $title, $subtitle, $_;
      
      # uncomment the next line if you want only the first match in
      # any given file (i.e. same as '-m 1' with grep):
      # close $fh;
    }
  };
  close($fh);
}

示例运行:

$ ./grep-md.pl ./ yada-yada
./file.md:5:Title:Subtitle:yada-yada
./file2.md:5:Another Title:And Another Subtitle:yada-yada
./sub/dir/several/levels/deep/file3.md:5:Third Title:File Three Subtitle:yada-yada

顺便说一句,这也可以编写用于find ... -exec查找文件,而不是使用 File::Find 来查找文件,如果这样做可能会更好......我主要以这种方式编写它来展示实现相同目标的多种不同方法最终目标。

相关内容