我有以下代码,列出了远匹配的模式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
我提出了另一个使用grep
without 的解决方案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