提问人:pathikrit 提问时间:10/11/2012 最后编辑:pathikrit 更新时间:11/27/2018 访问量:59550
如何在 grep 中为每个文件匹配一次?
How to match once per file in grep?
问:
是否有任何 grep 选项可以让我控制匹配总数,但在每个文件的第一个匹配时停止?
例:
如果我这样做,我会得到这个:grep -ri --include '*.coffee' 're' .
./app.coffee:express = require 'express'
./app.coffee:passport = require 'passport'
./app.coffee:BrowserIDStrategy = require('passport-browserid').Strategy
./app.coffee:app = express()
./config.coffee: session_secret: 'nyan cat'
如果我这样做了,我会得到这个:grep -ri -m2 --include '*.coffee' 're' .
./app.coffee:config = require './config'
./app.coffee:passport = require 'passport'
但是,我真正想要的是这个输出:
./app.coffee:express = require 'express'
./config.coffee: session_secret: 'nyan cat'
做是行不通的,因为我得到了这个-m1
grep -ri -m1 --include '*.coffee' 're' .
./app.coffee:express = require 'express'
尝试不使用 grep,例如这产生了:find . -name '*.coffee' -exec awk '/re/ {print;exit}' {} \;
config = require './config'
session_secret: 'nyan cat'
更新:如下所述,GNU grep -m 选项处理每个文件的计数,而 BSD
grep 的 -m
将其视为全局匹配计数
答:
我认为你可以做这样的事情
grep -ri -m1 --include '*.coffee' 're' . | head -n 2
例如,从每个文件中选择第一个匹配项,最多选择两个匹配项。
请注意,这需要您视为每个文件的匹配限制;GNU 确实这样做了,但 BSD 显然将其视为全局匹配限制。grep
-m
grep
grep
评论
-m1
对我来说,在全球第一场比赛中停止。无论如何,如果有数百万个匹配项,而我只想要其中的 100 个,那么这是低效的,因为在将结果引入之前,grep 仍然会进行前一百万个匹配项head
(BSD grep) 2.5.1-FreeBSD
-m
/usr/bin
/usr/bin/grep --version
grep (BSD grep) 2.5.1-FreeBSD
uname -s -r
Darwin 12.2.0
/usr/bin/grep
-m
我会这样做。awk
find . -name \*.coffee -exec awk '/re/ {print FILENAME ":" $0;exit}' {} \;
如果你不需要递归,你可以用 awk 来做:
awk '/re/ {print FILENAME ":" $0;nextfile}' *.coffee
或者,如果你使用的是当前足够多的 bash,则可以使用 globstar:
shopt -s globstar
awk '/re/ {print FILENAME ":" $0;nextfile}' **/*.coffee
评论
bash-3.2$ find . -name \*.coffee -exec awk '/re/ {print;exit}' {} \; config = require './config' session_secret: 'nyan cat'
find -exec
head
SIGPIPE
find
find
head
awk
bash
base-3.2$
bash
re
你可以在perl中轻松做到这一点,而且没有混乱的跨平台问题!
use strict;
use warnings;
use autodie;
my $match = shift;
# Compile the match so it will run faster
my $match_re = qr{$match};
FILES: for my $file (@ARGV) {
open my $fh, "<", $file;
FILE: while(my $line = <$fh>) {
chomp $line;
if( $line =~ $match_re ) {
print "$file: $line\n";
last FILE;
}
}
}
唯一的区别是你必须使用Perl风格的正则表达式,而不是GNU风格。它们没有太大区别。
你可以在Perl中使用File::Find来做递归部分,或者使用feed文件。find
find /some/path -name '*.coffee' -print0 | xargs -0 perl /path/to/your/program
使用 find 和 xargs。 查找每个 .coffee 文件,并为每个文件执行 -m1 grep
find . -print0 -name '*.coffee'|xargs -0 grep -m1 -ri 're'
测试 不带 -m1
linux# find . -name '*.txt'|xargs grep -ri 'oyss'
./test1.txt:oyss
./test1.txt:oyss1
./test1.txt:oyss2
./test2.txt:oyss1
./test2.txt:oyss2
./test2.txt:oyss3
添加 -m1
linux# find . -name '*.txt'|xargs grep -m1 -ri 'oyss'
./test1.txt:oyss
./test2.txt:oyss1
评论
find -print0
xargs -0
find -print0
xargs -0
find . -name \*.coffee -exec grep -m1 -i 're' {} \;
find 的 -exec 选项对每个匹配的文件运行一次命令(除非您使用 instead of ,这使它的作用类似于 xargs)。+
\;
因此,使用 ,您只需要选项 -l, --files-with-matches
。grep
所有这些关于 或 shell 脚本的答案都远离了这个问题。find
awk
评论
-l
--files-with-matches
评论
re
./app.coffee
config =
express =
grep -m1