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

私はあなたがこの答えが好きではないと確信しています。しかし、これはこれを行う正しい方法(いいえ。これ正しい方法ですが、多くの方法の1つだけです)。標準ツール(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の多くのオプション処理モジュールの1つを使用して実行できます(例:GetSelect::標準またはGetopt::長い)しかし、このスクリプトはこのウェブサイトに比べてすでに長すぎます。find使用せずにPerlですべてを書くことができます。ファイル::検索基準寸法。 3つのモジュールはすべてコア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文書。これらは読者に練習問題として残す。

出力例(スクリプト自体の一致行と「チョップ」パターンを囲むコンテキストの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

grepなしで他の解決策を思い出しました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

関連情報