Przekonaj grep, aby wypisał wszystkie linie, nie tylko te z dopasowaniami

121

Powiedz, że mam następujący plik:

$ cat test

test line 1
test line 2
line without the search word
another line without it
test line 3 with two test words
test line 4

Domyślnie grepzwraca każdy wiersz zawierający wyszukiwane hasło:

$ grep test test

test line 1
test line 2
test line 3 with two test words
test line 4

Przekazanie --colorparametru grepspowoduje, że podświetli on część wiersza pasującą do wyrażenia wyszukiwania, ale nadal zwraca tylko wiersze zawierające wyrażenie. Czy istnieje sposób, aby uzyskać grepwyjście każdej linii w pliku źródłowym, ale wyróżnić dopasowania?

Mój obecny straszny hack, aby to osiągnąć (przynajmniej w plikach, które nie mają ponad 10000 kolejnych wierszy bez dopasowań) to:

$ grep -B 9999 -A 9999 test test

Zrzut ekranu dwóch poleceń

Jeśli grepnie można tego osiągnąć, czy istnieje inne narzędzie wiersza polecenia, które oferuje tę samą funkcjonalność? Bawiłem się ack, ale wydaje się, że nie ma na to również opcji.

Michał Mrożek
źródło
1
Możliwy duplikat dla wielu witryn: stackoverflow.com/questions/981601/ ... Najlepsza odpowiedź jest taka sama w obu przypadkach.
Ciro Santilli 16 改造 中心 法轮功 六四 事件
1
Możesz użyć -C 9999zamiast. -A 9999 -B 9999Zawsze robię:grep -C 9999 pattern file
neo

Odpowiedzi:

182
grep --color -E "test|$" yourfile

To, co tu robimy, jest dopasowane do $wzoru i wzoru testowego, oczywiście $nie ma nic do pokolorowania, więc tylko wzór testowy zyskuje kolor. -EProstu włącza rozszerzonego dopasowania regex.

Możesz z niego łatwo utworzyć funkcję:

highlight () { grep --color -E "$1|$" "${@:1}" ; }
jacksonh
źródło
14
To cholernie niesamowite!
gvkv
3
I jako funkcję: highlight () { grep --color -E "$1|$" $2 ; }. Zastosowanie:highlight test yourfile
Stefan Lasiewski
2
@StefanLasiewski: "$2"należy również zacytować.
Dennis Williamson
6
Lepiej może być: highlight () { grep --color -E "$1|$" "$@" }co pozwala na pliki z białymi znakami w nazwach i wiele plików.
Mike DeSimone
15
@MikeDeSimone - Ale będzie to również miało miejsce "$1"w plikach. Użyjhighlight () { grep --color -E "$1|$" "${@:1}" }
Chris Down
39
ack --passthru --color string file

w przypadku Ubuntu i Debiana używaj ack-grep zamiast ack

ack-grep --passthru --color string file
Dennis Williamson
źródło
1
Och, to całkiem oczywiste na stronie podręcznika; Jestem niewidomy. Dzięki
Michael Mrozek
wzorzec dla grap może być wyraźnie określony za pomocą --regexp string; odpowiednikiem ack / ack-grep jest--match string
Rhubbarb
Aby symulować ack --passthruz perlem:grep_passthrough () { ! perl -ne "print; \$e ||= /$@/; END{{exit \$e}}"; }
Lucas Cimon
ack --passthrudziała również na strumieniach! Na przykładifconfig | ack --passthru inet
honkaboy,
12

Innym sposobem, aby to zrobić poprawnie i przenośnie za pomocą grep(oprócz używania dwóch wyrażeń regularnych z naprzemiennie jak w zaakceptowanej odpowiedzi) jest użycie wzorca zerowego (i odpowiednio łańcucha zerowego).
Powinno działać równie dobrze z obydwoma -Ei -Fprzełącznikami, ponieważ zgodnie ze standardem :

-E
    Match using extended regular expressions. 
    [...] A null ERE shall match every line.

i

-F
    Match using fixed strings.
    [...] A null string shall match every line.

To po prostu kwestia biegania

grep -E -e '' -e 'pattern' infile

i odpowiednio

grep -F -e '' -e 'string' infile
don_crissti
źródło
1
Lub po prostu grep -F -e '' -e 'string'.
Stéphane Chazelas
Działa to dla mnie z GNU grep 2.25, busybox grep i dziedzicznym zestawem narzędzi.
Stéphane Chazelas
OK, mój zły, seq 3 | grep -F -e '' -e 2wypisuje tylko 2 w 2.27 (i 2.26, ale nie 2.25 i nie w git head). Tylko z -F(nie bez lub z -E). IOW, tylko 2.26 i 2.27 wydają się mieć błąd i tylko z -F.
Stéphane Chazelas
Zauważ, że seq 3 | grep -F -e '' -e '' -e 2 wydaje się to również działać z 2.27
Stéphane Chazelas
1
Metoda podana w tej odpowiedzi jest prosta, poprawna i wydaje się dość szybka . Jak mówisz , działa automatycznie -F, zmniejszając potrzebę martwienia się o to, w jakich postaciach się pojawią string. Możesz wspomnieć --color; ponieważ ta odpowiedź unosi się na stronie (wyświetlana podczas głosowania), więcej osób może przeczytać ją przed innymi odpowiedziami.
Eliah Kagan
11

Mam następującą funkcję, której używam do takich rzeczy:

highlight () {
    perl -pe "s/$1/\e[1;31;43m$&\e[0m/g"
}

Wewnętrznie wygląda trochę brzydko, jest przyjemny i łatwy w użyciu, tak jak:

cat some_file.txt | highlight some_word

lub, dla nieco bardziej realistycznego przykładu:

tail -f console.log | highlight ERROR

Możesz zmienić kolory na dowolne (co może być trudne z grep - nie jestem pewien), zmieniając 1i 31oraz 43(po \e[) na inne wartości. Te kody do wykorzystania są wszystkie na miejscu , ale tutaj to szybki wstęp: na 1 pogrubienia tekstu, 31sprawia, że czerwone i 43daje żółtym tle. 32lub 33byłyby w różnych kolorach i 44lub 45byłyby w różnych środowiskach: masz pomysł. Możesz nawet sprawić, by mrugał (za pomocą a 5), jeśli masz na to ochotę.

To nie używa żadnych specjalnych modułów Perla, a Perl jest prawie wszechobecny, więc spodziewałbym się, że będzie działać prawie wszędzie. Rozwiązanie grep jest bardzo sprytne, ale przełącznik --color na grep nie jest dostępny wszędzie. Na przykład, właśnie wypróbowałem to rozwiązanie na boksie Solaris z uruchomionym bashem, innym uruchomionym ksh i moją lokalną maszyną Mac OS X z systemem zsh. Wszystko działało dobrze. grep --colorJednak Solaris dusił się w tym rozwiązaniu.

Ponadto potwierdzenie jest niesamowite i polecam go każdemu, kto go jeszcze nie odkrył, ale miałem problemy z instalacją go na kilku serwerach, na których pracuję. (Zapominam dlaczego: Myślę, że wymagało to modułów Perla).

Ponieważ nie sądzę, żebym kiedykolwiek pracował nad urządzeniem Unix, w którym nie zainstalowano Perla (z wyjątkiem systemów osadzonych, routerów Linksys itp.), Powiedziałbym, że jest to rozwiązanie uniwersalne .

ikonoklasta
źródło
3

Zamiast używać Grep, możesz użyć Less:

less file

Szukaj w ten sposób: /pattern Enter

Spowoduje to:

  1. przewiń do pierwszej pasującej linii
  2. wypisuje każdą linię, zaczynając od tego momentu
  3. zaznacz wszystkie mecze

Aby przewinąć do następnej pasującej linii: n

Aby przewinąć do poprzedniej pasującej linii: N

Aby przełączyć podświetlanie: Esc u

Możesz także zmienić kolor podświetlenia, jeśli chcesz.

Steven Penny
źródło
2

Możesz spróbować:

perl -MTERM::ANSIColor -nle '/pattern/ ? print colored($_, 'color') : print' test

Jednak niezbyt przenośny i nawet jeśli Perl jest zainstalowany, może być konieczne pobranie innego modułu. Ponadto pokoloruje całą linię, a nie tylko szukane słowo.

gvkv
źródło
2

ripgrep

Użyj ripgrepz jego --passthruparametrem:

rg --passthru pattern file.txt

Jest to jedno z najszybszych narzędzi greppingowych , ponieważ zostało zbudowane na silniku regularnym Rust, który wykorzystuje skończone automaty, SIMD i agresywne optymalizacje dosłowne, aby wyszukiwanie było bardzo szybkie.

--passthru - Wydrukuj zarówno pasujące, jak i niepasujące linie.

Innym sposobem osiągnięcia podobnego efektu jest modyfikacja wzorca w celu dopasowania pustego ciągu. Na przykład, jeśli szukasz używając, rg foowówczas użycie rg "^|foo"zamiast spowoduje wyemitowanie każdej linii w każdym przeszukiwanym pliku, ale podświetlone będą tylko wystąpienia foo. Ta flaga umożliwia takie samo zachowanie bez potrzeby modyfikowania wzorca.

kenorb
źródło
1

Jest o wiele łatwiejszy sposób na zrobienie tego dla GNU grep, ale nie sądzę, aby był przenośny (tj. BSD grep):

W rurze:

cat <file> | grep --color=always -z <query>

W pliku:

grep --color=always -z <query> <file>

Zasługa odpowiedź Cyrusa tutaj .

Erik Nomitch
źródło
1
To nie jest tak dobra odpowiedź, jak myślisz (i Cyrus). Powoduje to traktowanie całego pliku jako pojedynczej linii. Dlatego (1), jeśli plik jest bardzo duża, może istnieć możliwość przeprowadzenia z pamięci, oraz (2) jeśli plik nie zawiera wzór w ogóle , to nic nie będzie odtwarzany. I masz rację; nie jest powszechnie dostępny. (Ale z drugiej strony też --colornie jest standardem .)
G-Man,
W jakiej wersji grep jest obsługiwany? Na grep 2.5.4 -z nie wydaje się dostępny ...
Alex
1

Żadna z podanych dotychczas odpowiedzi nie stanowi przenośnego rozwiązania.

Tutaj jest przenośnym 1 funkcja powłoki ja już pisał w zamkniętym jako powtórzenie pytania, które nie wymaga narzędzi innych niż standardowe lub rozszerzenia niestandardowe zaopatrzone perl, ack, ggrep, gsed, bashi lubi ale tylko potrzebuje powłoki POSIX i POSIX obowiązkowych narzędzi sedoraz printf:

grepc()
{
  pattern=$1
  shift
  esc=$(printf "\033")
  sed 's"'"$pattern"'"'$esc'[32m&'$esc'[0m"g' "$@"
}

Możesz użyć tego w ten sposób:

grepc string_to_search [file ...]

Kolor podświetlenia można dostosować za pomocą jednego z tych kodów w argumencie polecenia sed (tutaj 32m jest zielony):

30m black
31m red
33m yellow
34m blue
35m magenta
36m cyan
37m white
7m reverse video

1 Tak długo, jak twój terminal obsługuje sekwencje specjalne kolorów ANSI.

jlliagre
źródło
0

sedWersja, działa na obu bashi ash.

#highlight
hsed(){
    local pattern="$1"
    shift
    local r=`echo -e '\e'[31m`
    local c=`echo -e '\e'[0m`
    sed "s:${pattern//:/\:}:$r\0$c:g" "$@"
}
Yurenchen
źródło
0

OP poprosił o to grepi JA POLECAM ; ale po trudach rozwiązania problemu sed, dla przypomnienia, oto proste rozwiązanie:

sed $'s/main/\E[31m&\E[0m/g' testt.c

lub

cat testt.c | sed $'s/main/\E[31m&\E[0m/g'

Pomaluje mainna czerwono.

  • \E[31m : sekwencja początkowa koloru czerwonego
  • \E[0m : gotowy znak koloru
  • & : dopasowany wzór
  • /g : wszystkie słowa w linii, nie tylko pierwsze
  • $'string' to ciągi bash z interpretowanymi znakami ucieczki

Jeśli chodzi o grep , działa również przy użyciu ^(początek linii) zamiast $(koniec linii). Przykład:

egrep "^|main" testt.c

I żeby pokazać ten zwariowany alias, którego NIE POLECAM , możesz nawet pozwolić na otwarte cytaty:

alias h='egrep -e"^|'
h main" testt.c
cat testt.c | h main"

cała praca! :) Nie martw się, jeśli zapomnisz zamknąć cytat, bash zapamięta cię „ciągłym znakiem linii”.

Dr Beco
źródło