일치하는 패턴을 나열 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