在 bash 中查找文件并与打印文件名匹配模式

在 bash 中查找文件并与打印文件名匹配模式

我有以下代码,列出了远匹配的模式ptrn,并在列表之前打印文件名(使用上下文选项-C NUM

find "$fdir" "${isufx[*]}" -type f -exec bash -c  \
  "grep --color -l '$ptrn' '{}'; grep --color -ni ${ictx[*]} '$ptrn' '{}'" \;

我同意这是一个怪物。我决定删除bash -c通话,结果是

  OFS=$IFS
  IFS=$'\n'
  for f in $(find "$fdir" ${isufx[*]} -type f); do
    grep -l "$ptrn" "$f" && grep -ni ${ictx[*]} "$ptrn" "$f"
  done
  IFS=$OFS

对以上有什么建议吗?我想在列表之前打印文件名,包含在==>和之间<==,并在文件名上方和下方各有一个空行。

在提出关于避免循环输出的建议之后find,我有:

  find "$fdir" ${isufx[*]} -type f |
    while read f; do
      grep -l "$ptrn" "$f" && grep -ni ${ictx[*]} "$ptrn" "$f"
    done

答案1

我很确定你不会喜欢这个答案,但这是A这样做的正确方法(不是正确的方法,但只是众多方法之一)。对于那些想知道当标准工具(如 grep)不能完全按照您希望的那样编写自己的自定义工具的人来说,这是一个示例 - 这就是 UNIX 的本来面目用过的。 (也因为我想知道解析和使用 grep 的 GREP_COLORS 变量有多困难......事实证明非常简单)

这是一个只需要分叉一次的脚本,find ... -exec并且可以一次性完成每个文件的所有操作,而不是多次传递。它处理任何有效的文件名,即使是包含换行符和其他空格的文件名。

将下面的脚本另存perl为,例如,context-grep.pl并使其可执行chmod +x context.pl。然后像这样运行它:

find "$fdir" "${isufx[*]}" -type f -exec ./context-grep.pl {} +

它将使用以下环境变量:

  • $ptrn用于搜索模式
  • $NUM要打印的上下文行数(可选,默认为 3)
  • $GREP_COLOR$GREP_COLORS使用相同的颜色代码grep(可选,默认为绿色)。

像往常一样,这些可以在命令行上指定,例如

NUM=5 ptrn='foo.*bar' find "$fdir" "${isufx[*]}" -type f -exec ./context-grep.pl {} +

脚本的正确选项处理可以使用 perl 的许多选项处理模块之一来完成(例如获取选择::标准或者Getopt::长)但是这个脚本对于这个网站来说已经太长了。整个事情可以用 Perl 编写,而不需要find使用文件::查找模块。所有这三个模块都是核心 Perl 库模块,并且包含在 Perl 中。

#!/usr/bin/perl

use strict;

# This script should use TERM::TERMCAP to get the actual
# colour codes for the current $TERM from the terminfo
# database, but I'll just hard-code it to use ANSI colour
# codes because almost everything is ansi-compatible these days.
# That's good enough for grep, so it's good enough for this.

###
### variable setup and related stuff
###

my $sgr0 = "\033[m\017";
my $colour = "\033[01;32m"; # default to green

# If either of grep's colour env vars are defined, use
# them instead. (the newer $GREP_COLORS is checked last,
# so has precedence over $GREP_COLOR)
if ($ENV{'GREP_COLOR'}) {
  $colour = "\033[$ENV{'GREP_COLOR'}m";
};

if ($ENV{'GREP_COLORS'}) {
  # e.g. ms=01;31:mc=01;31:sl=:cx=:fn=35:ln=32:bn=32:se=36
  # This script really only cares about the ms value
  # It wouldn't be hard to make it use `mc` as well to print the
  # context lines in a different colour than the match line.
  my @GC = split /:/, $ENV{'GREP_COLORS'};
  foreach (@GC) {
    if (m/^ms/) {
      my (undef,$c) = split /=/;
      $colour = "\033[${c}m";
      last;
    }
  };
};

my $search=$ENV{'ptrn'};
my @context;

my $NUM=3; # default to 3 lines of context
$NUM = $ENV{'NUM'} if (defined($ENV{'NUM'}));

my $last = -1;

my $first_match=1;

###
### main loop, process the input file(s)
###

while(<>) {
  chomp;

  if ($. <= $last) {
    # current line is an AFTER context line, print it
    printf "%s%s%s\n", $colour, $_, $sgr0;

  } elsif (m/$search/) {
    # We've found a match! handle it.

    # print filename like head & tail does if this is the
    # first match we've found in the current file.
    if ($first_match) {
      printf "\n==> %s <==\n\n", $ARGV;
      $first_match=0;
    };

    # print the remembered BEFORE context lines
    foreach my $l (@context) {
      printf "%s%s%s\n", $colour, $l, $sgr0;
    };

    # print current line
    printf "%s%s%s\n", $colour, $_, $sgr0;

    # clear the context array 
    @context=();

    # set $last so we can print the AFTER context lines
    $last = $. + $NUM;

  } else {
    # remember the last $NUM lines of context
    push @context, $_;                     # add current C line
    shift @context if ($#context >= $NUM); # remove first C line
  };

  # reset $last, $first_match, and the input record counter
  # ($. - equivalent to awk's NR) on every EOF
  if (eof) {
    close(ARGV);
    $last = -1;
    $first_match=1;
  };
};

错误:绝对没有错误处理。或选项处理。或帮助/使用消息。或 POD 文档。这些留给读者作为练习。

示例输出(来自脚本本身的匹配行和围绕模式“chomp”的 2 行上下文):

$ NUM=2 ptrn=chomp find . -type f -name '*.pl' -exec ./context-grep.pl {} +

==> ./context-grep.pl <==


while(<>) {
  chomp;

  if ($. <= $last) {

我的export GREP_COLOR='0;33'所以~/.bashrc匹配行和上下文行都以黄色打印。文件名以终端的默认文本颜色(黑底白字)打印。

答案2

我提出了另一个使用grepwithout 的解决方案find

echo ""
grep -rl ${isufx[@]} "$ptrn" $fdir |
  while read f; do
    echo -e $(tput setaf 46)"==> $f <==\n"$(tput sgr0)
    grep -ni ${ictx[@]} "$ptrn" "$f"
    echo ""
  done

相关内容