Ogranicz wyjście grep do krótkich linii

8

Często używam grep, aby znaleźć pliki mające określony wpis, taki jak ten:

grep -R 'MyClassName'

Dobrą rzeczą jest to, że zwraca pliki, ich zawartość i oznacza znaleziony ciąg na czerwono. Złe jest to, że mam również ogromne pliki, w których cały tekst jest zapisany w jednym dużym pojedynczym wierszu. Teraz grep wyświetla zbyt wiele wyników podczas wyszukiwania tekstu w tych dużych plikach. Czy istnieje sposób, aby ograniczyć wynik do na przykład 5 słów po lewej i po prawej stronie? A może ograniczyć wydruk do 30 liter w lewo i w prawo?

Sokrates
źródło
3
Prześlij wyniki przezcut
Rinzwind
Powiedzmy, że wzór, którego szukasz, znajduje się na pozycji 50, ale powiedziałeś, że chcesz tylko 30 liter. Co zatem chcesz zrobić? Zignorować tę linię lub dołączyć ją do wyniku, ale przyciąć? Co dokładnie chcesz ograniczyć - wyszukiwanie lub same linie?
Sergiy Kolodyazhnyy 18.04.18
1
@Rinzwind Nie do końca rozumiem, co chcesz osiągnąć cut, ponieważ dzieli tylko według separatora lub liczby znaków. Chociaż kiedy znajdę linię MyClassName, może ona znajdować się w dowolnym miejscu linii i nie zawsze w tej samej pozycji. Co więcej, może występować odmiana znaków z przodu i z tyłu, co przerywa możliwość podziału według separatora.
Sokrates,
1
@SergiyKolodyazhnyy Gdy MyClassNamezostanie znaleziona linia dodatnia , chcę uzyskać nazwę pliku i znaki x po lewej i prawej stronie. x to dowolny numer, który podam, na przykład 30. Resztę zawartości pliku należy zignorować. Ma to na celu uzyskanie kontekstu dla pasujących plików i ograniczenie przeciążenia.
Sokrates,
1
@Rinzwind Jaki rodzaj niestandardowego separatora proponujesz ze cutjeśli istnieją trzy pliki z następującego kodu: oiadfaosuoianavMyClassNameionaernaldfajdi /(/&%%§%/(§(/MyClassName&((/$/$/(§/$&i public class MyClassName { public static void main(String[] args) { } }?
Sokrates,

Odpowiedzi:

15

grepsam ma tylko opcje kontekstu opartego na liniach. Alternatywą jest ten post SU :

Obejściem tego problemu jest włączenie opcji „tylko dopasowanie”, a następnie użycie mocy RegExp do grepowania nieco więcej niż tekstu:

grep -o ".\{0,50\}WHAT_I_M_SEARCHING.\{0,50\}" ./filepath

Oczywiście, jeśli użyjesz podświetlania kolorów, zawsze możesz ponownie grepować, aby pokolorować tylko prawdziwe dopasowanie:

grep -o ".\{0,50\}WHAT_I_M_SEARCHING.\{0,50\}"  ./filepath | grep "WHAT_I_M_SEARCHING"

Jako kolejną alternatywę zasugerowałem foldtekst, a następnie grep, na przykład:

fold -sw 80 input.txt | grep ...

Ta -sopcja spowoduje, że foldsłowa wypychają do następnego wiersza zamiast przerywać między nimi.

Lub użyj innego sposobu, aby podzielić dane wejściowe na wiersze na podstawie struktury danych wejściowych. (Na przykład post SU dotyczył JSON, więc używanie jqitp. Do ładnego drukowania i grep... lub po prostu jqsamodzielne filtrowanie ... byłoby lepsze niż jedna z dwóch podanych powyżej opcji).


Ta metoda GNU awk może być szybsza:

gawk -v n=50 -v RS='MyClassName' '
  FNR > 1 { printf "%s: %s\n",FILENAME, p prt substr($0, 0, n)}
  {p = substr($0, length - n); prt = RT}
' input.txt
  • Powiedz awk, aby podzielił rekordy na wzór, który nas interesuje ( -v RS=...), i liczbę znaków w kontekście ( -v n=...)
  • Każdy rekord po pierwszym rekordzie ( FNR > 1) to taki, w którym awk znalazł dopasowanie do wzorca.
  • Dlatego drukujemy nkońcowe znaki z poprzedniej linii ( p) i nwiodące znaki z bieżącej linii ( substr($0, 0, n)), wraz ze dopasowanym tekstem dla poprzedniej linii (która jest prt)
    • ustawiamy pi prt po wydrukowaniu, więc ustawiona wartość jest używana w następnym wierszu
    • RT jest GNUizmem, dlatego jest specyficzny dla GNU awk.

W przypadku wyszukiwania rekurencyjnego może:

find . -type f -exec gawk -v n=50 -v RS='MyClassName' 'FNR>1{printf "%s: %s\n",FILENAME, p prt substr($0, 0, n)} {p = substr($0, length-n); prt = RT}' {} +
muru
źródło
2
Ok, to działa. Wydaje się, że Regex to prawidłowe podejście, więc dziękuję za to. Czas przetwarzania jest jednak dość duży. Bez Regex, jak w moim powyższym poście, zajmuje on 4.912 s, a z Regex, jak w twoim poście, zajmuje 3m39.312s.
Sokrates
1
@ Sokrates sprawdza, czy dodana powyżej metoda awk działa lepiej
mur
1
foldMetoda może być stosowana tylko wtedy, gdy jesteś pewien, że szukany ciąg nie pojawia się na granicy, w przeciwnym razie byłoby uzyskać ukryte grep.
Melebius
1
@muru Dzięki za sugestię gawk. Niestety sugerowane polecenie z findlosowymi danymi wyjściowymi i bez nazw plików po uruchomieniu w moim systemie. Co więcej, nie jestem wystarczająco biegły, awkaby poprawnie przeanalizować polecenie. Obecnie Regex w połączeniu z greprozwiązuje problem może nie szybko, ale niezawodnie. Jeszcze raz wielkie dzięki.
Sokrates
1
@Sokrates Myślę, że udało mi się naprawić polecenie awk. Mój model mentalny był błędny w kwestii tego, która linia RTi prefiks itp. Miały być użyte.
muru
1

Używanie dopasowania tylko w połączeniu z niektórymi innymi opcjami (patrz poniżej) może być bardzo zbliżone do tego, czego szukasz, bez narzutu przetwarzania wyrażenia regularnego wymienionego w drugiej odpowiedzi

grep -RnHo 'MyClassName'
  • n wyjście numeryczne, pokaż numer wiersza dopasowania
  • H nazwa pliku, pokaż nazwę pliku na początku linii dopasowania
  • o tylko dopasowania, pokaż tylko dopasowany ciąg, a nie całą linię
Robert Riedl
źródło
Chociaż prawdą jest, że wynik znajduje się znacznie szybciej, brakuje informacji. Pokazana jest ścieżka do pliku, numer linii, ale tekst jest tylko moim początkowym wyszukiwaniem MyClassName. Dlatego brakuje kontekstu.
Sokrates,
grep -RnHo "MyClassName"i grep -Rno "MyClassName"mają taką samą wydajność.
Sokrates,
Wyjście @Sokrates nie jest takie samo bez H w tym samym katalogu
Robert Riedl
-oFlaga może być interesujące, jeśli regex miał jakieś części zmiennej. W przypadku stałego ciągu nie ma sensu go drukować za każdym razem. OP najprawdopodobniej interesuje się bliskim kontekstem.
Melebius
1
@Sokrates, prawda - brakuje kontekstu, ale myślałem, że o to chodzi? Ogranicz wydajność? Możesz dodać kontekst ponownie, dodając linie przed ( -B 1) lub po ( -A 1). Przepraszam, że nie mogłem pomóc.
Robert Riedl,