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

나는 당신이 이 답변을 좋아하지 않을 것이라고 확신합니다. 그러나 이것은이를 수행하는 올바른 방법(아닙니다.이것올바른 방법이지만 많은 방법 중 하나일 뿐입니다). 표준 도구(예: 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의 많은 옵션 처리 모듈 중 하나를 사용하여 수행할 수 있습니다(예:GetSelect::표준또는Getopt::긴) 하지만 이 스크립트는 이 웹사이트에 비해 이미 너무 깁니다. find사용 하지 않고도 Perl로 모든 것을 작성할 수 있습니다.파일::찾기기준 치수. 세 가지 모듈은 모두 핵심 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

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

관련 정보