Jak znaleźć wzorki w wielu wierszach za pomocą grep?
Chcę znaleźć pliki, które mają " abc " i " efg " w tej kolejności, a te dwa łańcuchy są w różnych liniach w tym pliku. Np: plik o treści:
blah blah..
blah blah..
blah abc blah
blah blah..
blah blah..
blah blah..
blah efg blah blah
blah blah..
blah blah..
Powinny być dopasowane.
21 answers
Grep nie jest wystarczający do tej operacji.
Pcregrep , który znajduje się w większości współczesnych systemów Linux może być używany jako
pcregrep -M 'abc.*(\n|.)*efg' test.txt
Istnieje również nowszy pcre2grep . Oba są dostarczane przez PCRE project .
Pcre2grep jest dostępny dla Mac OS X poprzez porty Mac jako część portu pcre2
:
% sudo port install pcre2
I przez Homebrew jako:
% brew install pcre
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-12 21:38:21
Nie jestem pewien, czy jest to możliwe z grep, ale sed sprawia, że jest to bardzo łatwe:
sed -e '/abc/,/efg/!d' [file-with-content]
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-13 20:01:52
Oto rozwiązanie inspirowane tą odpowiedzią :
-
Jeśli ' abc 'i' efg ' mogą być w tej samej linii:
grep -zl 'abc.*efg' <your list of files>
-
Jeśli " abc " i " efg " muszą być w różnych wierszach:
grep -Pzl '(?s)abc.*\n.*efg' <your list of files>
Params:
-z
traktuj wejście jako zbiór linii, z których każda zakończona jest bajtem zerowym, a nie znakiem nowej linii. tzn. grep zagraża wejściu jako jedna duża linia.-l
wypisuje nazwę KAŻDEGO pliku wejściowego, z którego wyjście Zwykle zostały wydrukowane.(?s)
Aktywuj PCRE_DOTALL, co oznacza, że '.'wyszukuje dowolny znak lub nowy wiersz.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-14 23:50:26
Sed powinien wystarczyć, jak wspomniano powyżej,
Zamiast !d możesz po prostu użyć p do wydruku:
sed -n '/abc/,/efg/p' file
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-28 22:21:05
W dużej mierze polegałem na pcregrep, ale z nowszym grep nie trzeba instalować pcregrep dla wielu jego funkcji. Wystarczy użyć grep -P
.
W przykładzie pytania OP, myślę, że następujące opcje działają ładnie, z drugim najlepiej pasującym jak rozumiem pytanie:
grep -Pzo "abc(.|\n)*efg" /tmp/tes*
grep -Pzl "abc(.|\n)*efg" /tmp/tes*
Skopiowałem tekst jako /tmp / test1 i usunąłem 'g' i zapisałem jako / tmp / test2. Oto wyjście pokazujące, że pierwszy pokazuje dopasowany ciąg znaków, a drugi pokazuje tylko nazwę pliku (typowy -o pokazuje dopasowanie, a typowe-l pokazuje tylko nazwę pliku). Zauważ, że " z "jest konieczne dla multiline i" (.|\n) "oznacza dopasowanie albo" cokolwiek innego niż newline "albo" newline " - tzn. cokolwiek:
user@host:~$ grep -Pzo "abc(.|\n)*efg" /tmp/tes*
/tmp/test1:abc blah
blah blah..
blah blah..
blah blah..
blah efg
user@host:~$ grep -Pzl "abc(.|\n)*efg" /tmp/tes*
/tmp/test1
Aby sprawdzić, czy Twoja wersja jest wystarczająco Nowa, Uruchom man grep
i sprawdź, czy coś podobnego pojawia się w pobliżu góry:
-P, --perl-regexp
Interpret PATTERN as a Perl regular expression (PCRE, see
below). This is highly experimental and grep -P may warn of
unimplemented features.
Pochodzi z GNU grep 2.10.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-29 15:27:51
Można to łatwo zrobić, używając tr
, Aby zastąpić nowe linie innym znakiem:
tr '\n' '\a' | grep 'abc.*def' | tr '\a' '\n'
Tutaj używam znaku alarmu, \a
(ASCII 7)zamiast nowej linii.
To prawie nigdy nie znajduje się w Twoim tekście, a {[3] } może dopasować go do .
, lub dopasować konkretnie do \a
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-09 00:41:57
Możesz to zrobić bardzo łatwo, jeśli możesz użyć Perla.
perl -ne 'if (/abc/) { $abc = 1; next }; print "Found in $ARGV\n" if ($abc && /efg/); }' yourfilename.txt
Można to zrobić za pomocą jednego wyrażenia regularnego, ale wiąże się to z zapisaniem całej zawartości pliku w jeden ciąg znaków, co może zająć zbyt dużo pamięci przy dużych plikach. Dla kompletności, oto ta metoda:
perl -e '@lines = <>; $content = join("", @lines); print "Found in $ARGV\n" if ($content =~ /abc.*efg/s);' yourfilename.txt
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-17 16:51:33
Nie wiem jak bym to zrobił z grepem, ale zrobiłbym coś takiego z awk:
awk '/abc/{ln1=NR} /efg/{ln2=NR} END{if(ln1 && ln2 && ln1 < ln2){print "found"}else{print "not found"}}' foo
Musisz jednak uważać, jak to robisz. Czy chcesz, aby Wyrażenie regularne pasowało do podłańcucha lub całego słowa? Dodaj znaczniki \w odpowiednio. Ponadto, chociaż jest to ściśle zgodne z tym, jak podałeś przykład, to nie działa, gdy abc pojawia się po raz drugi po efg. Jeśli chcesz się tym zająć, dodaj if odpowiednio w /abc / case itp.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-21 20:25:35
Awk one-liner:
awk '/abc/,/efg/' [file-with-content]
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-27 16:29:26
Kilka dni temu opublikowałem alternatywę grep, która obsługuje to bezpośrednio, albo poprzez dopasowanie wieloliniowe, albo za pomocą warunków - mam nadzieję, że jest przydatna dla niektórych osób szukających tutaj. Tak wyglądałyby polecenia dla przykładu:
Multiline: sift -lm 'abc.*efg' testfile
Warunki: sift -l 'abc' testfile --followed-by 'efg'
Można również określić ,że ' efg 'musi podążać za 'abc' w określonej liczbie wierszy:sift -l 'abc' testfile --followed-within 5:'efg'
Więcej informacji znajdziesz na stronie sift-tool.org .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-06 00:08:54
Niestety nie możesz. z grep
docs:
Grep przeszukuje nazwane pliki wejściowe (lub standardowe wejście, jeśli żadne pliki nie są nazwane, lub jeśli jako nazwę pliku podano pojedynczy myślnik-minus ( - )) w poszukiwaniu linii zawierających dopasowanie do podanego wzorca.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-21 20:24:15
Podczas gdy opcja sed jest najprostsza i najłatwiejsza, jednoliniowy LJ nie jest niestety najbardziej przenośny. Ci, którzy utknęli z wersją C Shell, będą musieli uciec z grzywki:
sed -e '/abc/,/efg/\!d' [file]
To niestety nie działa w bash et al.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-27 16:57:36
Jeśli chcesz używać kontekstów, możesz to osiągnąć wpisując
grep -A 500 abc test.txt | grep -B 500 efg
Wyświetli wszystko pomiędzy "abc" i "efg", o ile znajdują się one w odległości 500 linii od siebie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-10 15:48:58
Jeśli potrzebujesz, aby oba słowa były blisko siebie, na przykład nie więcej niż 3 linie, możesz to zrobić:
find . -exec grep -Hn -C 3 "abc" {} \; | grep -C 3 "efg"
Ten sam przykład, ale filtrowanie tylko *.pliki txt:
find . -name *.txt -exec grep -Hn -C 3 "abc" {} \; | grep -C 3 "efg"
A także możesz zastąpić grep
poleceniem egrep
Jeśli chcesz również znaleźć z wyrażeniami regularnymi.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-04 22:47:32
#!/bin/bash
shopt -s nullglob
for file in *
do
r=$(awk '/abc/{f=1}/efg/{g=1;exit}END{print g&&f ?1:0}' file)
if [ "$r" -eq 1 ];then
echo "Found pattern in $file"
else
echo "not found"
fi
done
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-22 00:02:25
Możesz użyć grepa okrywając, że nie jesteś zainteresowany sekwencją wzorca.
grep -l "pattern1" filepattern*.* | xargs grep "pattern2"
Przykład
grep -l "vector" *.cpp | xargs grep "map"
grep -l
znajdzie wszystkie pliki pasujące do pierwszego wzorca, a xargs wykona grep dla drugiego wzorca. Mam nadzieję, że to pomoże.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-24 17:07:01
With silver searcher :
ag 'abc.*(\n|.)*efg'
Podobne do odpowiedzi okaziciela pierścienia, ale zamiast tego z ag. Prędkość zalety silver searcher może zabłysnąć tutaj.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-13 21:04:10
Jako alternatywa dla odpowiedzi Balu Mohan, możliwe jest wymuszenie kolejności wzorców za pomocą tylko grep
, head
i tail
:
for f in FILEGLOB; do tail $f -n +$(grep -n "pattern1" $f | head -n1 | cut -d : -f 1) 2>/dev/null | grep "pattern2" &>/dev/null && echo $f; done
Ta nie jest zbyt ładna. Sformatowany bardziej czytelnie:
for f in FILEGLOB; do
tail $f -n +$(grep -n "pattern1" $f | head -n1 | cut -d : -f 1) 2>/dev/null \
| grep -q "pattern2" \
&& echo $f
done
Wyświetli nazwy wszystkich plików, w których "pattern2"
pojawia się po "pattern1"
, lub gdzie oba pojawiają się w tej samej linii :
$ echo "abc
def" > a.txt
$ echo "def
abc" > b.txt
$ echo "abcdef" > c.txt; echo "defabc" > d.txt
$ for f in *.txt; do tail $f -n +$(grep -n "abc" $f | head -n1 | cut -d : -f 1) 2>/dev/null | grep -q "def" && echo $f; done
a.txt
c.txt
d.txt
Wyjaśnienie
-
tail -n +i
- wypisuje wszystkie linie poi
th, włącznie z -
grep -n
- prepend dopasowanie linii z ich numerami linii -
head -n1
- wypisuje tylko pierwszy wiersz -
cut -d : -f 1
- wyświetla pierwszą kolumnę cięcia używając:
jako ogranicznika -
2>/dev/null
- silencetail
Wyjście błędu, które występuje, gdy wyrażenie$()
zwróci puste -
grep -q
- ciszagrep
i zwracamy natychmiast, jeśli dopasowanie zostanie znalezione, ponieważ interesuje nas tylko kod wyjścia
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-24 14:36:11
To też powinno zadziałać?!
perl -lpne 'print $ARGV if /abc.*?efg/s' file_list
$ARGV
zawiera nazwę bieżącego pliku podczas odczytu z przeszukiwania modyfikatora file_list
/s
przez nowy wiersz.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-18 20:37:48
Filepattern {[4] } jest ważny, aby zapobiec sprawdzaniu katalogów. Oczywiście jakiś test może temu zapobiec.
for f in *.sh
do
a=$( grep -n -m1 abc $f )
test -n "${a}" && z=$( grep -n efg $f | tail -n 1) || continue
(( ((${z/:*/}-${a/:*/})) > 0 )) && echo $f
done
The
grep -n -m1 abc $f
Wyszukuje maksymalnie 1 pasujące i zwraca (- N) numer linii. Jeśli znaleziono dopasowanie (test-N...) find the last match of efg (find all and take the last with tail - N 1).
z=$( grep -n efg $f | tail -n 1)
Else continue.
Ponieważ wynik jest czymś w rodzaju 18:foofile.sh String alf="abc";
musimy odciąć od": "aż do końca linii.
((${z/:*/}-${a/:*/}))
Powinien zwrócić wynik dodatni, jeśli ostatni mecz drugiego wyrażenia minął pierwszy mecz pierwszego.
Następnie zgłaszamy nazwę pliku echo $f
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-15 01:02:54
To powinno zadziałać:
cat FILE | egrep 'abc|efg'
Jeśli jest więcej niż jedno dopasowanie, możesz odfiltrować używając grep-v
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-09 11:37:03