在 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
我很確定您不會喜歡這個答案,但這是一種正確的方法(不是正確的方法,而是眾多方法之一)。對於那些想知道當標準工具(如 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::Std或Getopt::Long )之一來完成對腳本的正確選項處理,但是這個腳本對於這個站點來說已經太長了。整個事情可以用 perl 編寫,而不需要
find
File ::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; }; };
BUGS:絕對沒有錯誤處理。或選項處理。或幫助/使用資訊。或 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
所以匹配的行和上下文行以黃色列印。文件名以終端的預設文本顏色(黑底白字)列印。