Jak wydrukować wszystkie linie po dopasowaniu do końca pliku?


48

Plik wejściowy 1 to:

dog 123 4335
cat 13123 23424 
deer 2131 213132
bear 2313 21313

Daję dopasowaniu wzór z other file(jak dog 123 4335z pliku 2).

Dopasowuję wzór linii dog 123 4335i po wydrukowaniu wszystkich linii bez linii dopasowania, mój wynik to:

cat 13123 23424
deer 2131 213132
bear 2313 21313

Jeśli użyjesz tylko bez adresu linii, użyj tylko wzoru, na przykład 1s jak dopasować i wydrukować linie?


Czy inny plik może zawierać tylko jeden wzorzec do wyszukania lub jeden w wierszu i rozpocząć wyszukiwanie od linii znajdującej się najpierw w szukanym pliku?
Ciro Santilli 14 改造 中心 法轮功 六四 事件

Odpowiedzi:


27

Zakładając, że chcesz dopasować całą linię do swojego wzoru, z GNU sed, działa to:

sed -n '/^dog 123 4335$/ { :a; n; p; ba; }' infile

Standardowy odpowiednik:

sed -ne '/^dog 123 4335$/{:a' -e 'n;p;ba' -e '}' infile

Z następującymi danymi wejściowymi ( infile):

cat 13123 23424 
deer 2131 213132
bear 2313 21313
dog 123 4335
cat 13123 23424 
deer 2131 213132
bear 2313 21313

Dane wyjściowe to:

cat 13123 23424 
deer 2131 213132
bear 2313 21313

Wyjaśnienie:

  • /^dog 123 4335$/ szuka żądanego wzoru.
  • :a; n; p; ba;to pętla, która pobiera nowy wiersz z input ( n), drukuje go ( p) i rozgałęzia z powrotem, aby oznaczyć a :a; ...; ba;.

Aktualizacja

Oto odpowiedź, która zbliża się do twoich potrzeb, tj. Wzorzec w pliku 2, grepowanie z pliku 1:

tail -n +$(( 1 + $(grep -m1 -n -f file2 file1 | cut -d: -f1) )) file1

Osadzony grep i cut znajdują pierwszą linię zawierającą wzór z pliku2, ten numer linii plus jeden jest przekazywany do ogona, plus jeden służy do pominięcia linii ze wzorem.

Jeśli chcesz zacząć od ostatniego meczu zamiast pierwszego, byłoby to:

tail -n +$(( 1 + $(grep -n -f file2 file1 | tail -n1 | cut -d: -f1) )) file1

Pamiętaj, że nie wszystkie wersje ogona obsługują notację plusa.


To pierwszy przykład komend n i p w sed, które widziałem, i nie mam ochoty posunąć sed zbyt daleko. Wydaje się (z moich krótkich testów), że sed -n '/^dog 123 4335$/ { :a; p; n; ba; }' infile(przy przełączonych p i n) z powodzeniem zawiera również pasującą linię.
Josiah Yoder

26

Jeśli masz dość krótki grepsam plik , może działać:

grep -A5000 -m1 -e 'dog 123 4335' animals.txt

Domyślam się, że 5000 jest „dość krótkie”, ponieważ grepznajduje pierwsze dopasowanie i wysyła je wraz z następnymi 5000 liniami (plik nie musi mieć tak wielu). Jeśli nie chcesz samego dopasowania, musisz je odciąć, np

grep -A5000 -m1 -e 'dog 123 4335' animals.txt | tail -n+2


Jeśli nie chcesz pierwszego, ale ostatniego dopasowania jako separatora, możesz użyć tego:

tac animals.txt | sed -e '/dog 123 4335/q' | tac

Ta linia odczytuje animals.txtw odwrotnej kolejności linie i wyjścia do linii włącznie z linią, dog 123 4335a następnie ponownie cofa, aby przywrócić prawidłową kolejność.

Ponownie, jeśli nie potrzebujesz dopasowania w wyniku, dołącz ogon. (Możesz także skomplikować wyrażenie sed, aby odrzucić jego bufor przed zakończeniem.)


W moim teście GNU grep 3.0 nie wyświetla więcej niż 132 wierszy w kontekście późniejszym (niezależnie od określonej wartości).
ruvim

22

W praktyce prawdopodobnie używałbym odpowiedzi Aet3miirah przez większość czasu, a odpowiedź Alexeya jest cudowna, gdy chce się poruszać po liniach (również działa z less). OTOH, naprawdę podoba mi się inne podejście (które jest rodzajem odwróconej odpowiedzi Gillesa :

sed -n '/dog 123 4335/,$p'

Po wywołaniu z -nflagą sednie drukuje domyślnie linii, które przetwarza. Następnie używamy 2-adresowego formularza, który mówi, aby zastosować polecenie od dopasowania linii /dog 123 4335/do końca pliku (reprezentowanego przez $). Polecenie p, o którym mowa , drukuje bieżący wiersz. Oznacza to więc „wydrukuj wszystkie linie od jednego pasującego /dog 123 4335/do końca”.


3
To drukuje doglinię, która nie jest tutaj pożądana.
Stéphane Chazelas,

1
To wygląda na najlepszą odpowiedź (i działa w moim przypadku), ale musiałoby zostać dostosowane, aby pominąć również dopasowaną linię.
Pavel Šimerda

1
sed -n '/ dog 123 4335 /, $ p' | sed '1d' usunie linię psów
Kemin Zhou

1
sed -n '/dog 123 4335/,$p' | tail -n +2usunie również mecz
gilad mayani

15
sed -e '1,/dog 123 4335/d' file1

Jeśli chcesz odczytać wzorzec z pliku, zastąp go poleceniem sed. Jeśli plik zawiera wzorzec sed:

sed -e "1,/$(cat file2)/d" file1

Jeśli plik zawiera dosłowny ciąg do wyszukania, wpisz wszystkie znaki specjalne. Zakładam, że plik zawiera jedną linię.

sed -e "1,/$(sed 's/[][\\\/^$.*]/\\&/g' file2)/d" file1

Jeśli chcesz, aby dopasowaniem była cała linia, a nie tylko podciąg, zawiń wzór ^…$.

sed -e "1,/^$(sed 's/[][\\\/^$.*]/\\&/g' file2)\$/d" file1

6
To nie zadziała, jeśli wzór będzie w pierwszej linii. GNU sedma 0,/dog.../dna to.
Stéphane Chazelas,

14

$ more +/"dog 123 4335" file1


4
Działa również z less.
brandizzi

3
sprytny na terminalu, ale tak naprawdę nie działa, jeśli wpiszesz go w coś innego tac.
jcomeau_ictx

używam go w ten sposób, $ więcej + / „dopasuj moje słowa” plik1 >> plik2
AMB

1
Być może +został zastąpiony przez -pPOSIX 7: pubs.opengroup.org/onlinepubs/9699919799/utilities/more.html, ale nie został jeszcze zaimplementowany w util-linux 2.20.1. I to również drukuje skipping..i kilka nowych linii (do stderr oczekuję, więc może być w porządku).
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

może od tamtej pory wszystko się zmieniło? mój komentarz uzyskał 3 głosy poparcia, więc mogło być istotne w tym czasie ...
jcomeau_ictx


5

Jednym sposobem jest użycie awk:

awk 'NR==FNR{a[$0];next}f;($0 in a){f=1}'  file2 file1

gdzie plik2 zawiera wzorce wyszukiwania. Po pierwsze, cała zawartość pliku2 jest przechowywana w tablicy „a”. Podczas przetwarzania pliku1 każdy wiersz jest sprawdzany względem tablicy i drukowany tylko wtedy, gdy nie jest obecny.


Myślę, że OP chce wypisać każdy wiersz zgodny ze wzorem.
Thor

@Thor: dziękuję za zwrócenie uwagi, zaktualizowałem teraz ...
Guru

Ładnie wykonane :).
Thor

5

Jeśli dane wejściowe są zwykłym plikiem możliwym do zobaczenia :

Z GNU grep:

{ grep  -xFm1 'dog 123 4335' >&2
  cat; } <infile 2>/dev/null >outfile

Z sed:

{ sed -n '/^dog 123 4335$/q'
  cat; } <infile >outfile

GNU o grepnazwie w / -mopcja przerwie wprowadzanie podczas dopasowania - i opuści swoje (możliwe do zobaczenia) wejście fd natychmiast po punkcie, w którym znalazł swoje ostatnie dopasowanie. Tak więc wywołanie grepw / -m1znajduje pierwsze wystąpienie wzorca w pliku i pozostawia przesunięcie wejściowe dokładnie w odpowiednim miejscu catdo zapisania wszystkiego po pierwszym dopasowaniu wzorca do pliku na standardowe wyjście.

Nawet bez GNU grepmożesz zrobić dokładnie to samo z kompatybilnym z POSIX sed- kiedy sed quits jest określony, aby pozostawić przesunięcie wejściowe dokładnie tam, gdzie to robi. GNU sednie jest jednak w ten sposób zgodny ze standardami, więc powyższe prawdopodobnie nie będzie działać z GNU, sedchyba że wywołasz go za pomocą -uprzełącznika.


Uwaga: sedpokazane tutaj współdzielenie strumienia nie jest specjalnie (choć tak, wspomniany standard podaje konkretny przykład sedjako narzędzie, które jest w ten sposób zdolne) pokazanego swobodnego przepływu pracy. w szczególności wszystkie standardowe narzędzia są przeznaczone i określone do współpracy i współdzielenia pozycji kursorów strumieni wejściowych, nie powodując przy tym żadnego przetwarzania żadnego następnego czytnika. grep -qpowinien to zrobić; po cichu greppowinien powrócić, gdy tylko zostanie znalezione dopasowanie w danych wejściowych, a wszelkie pozostałe dane wejściowe nie powinny być standardowo zużywane.
mikeserv

4

Moja odpowiedź na pytanie w temacie, bez zapisywania wzoru w drugim pliku. Oto mój plik testowy:

$ cat animals.txt 
cat 13123 23424 
deer 2131 213132
bear 2313 21313
dog 123 4335
cat 13123 23424 
deer 2131 213132
bear 2313 21313

GNU sed:

 $ sed '0,/^dog 123 4335$/d' animals.txt 
 cat 13123 23424 
 deer 2131 213132
 bear 2313 21313

Perl:

$ perl -ne 'print unless 1.../^dog 123 4335$/' animals.txt
cat 13123 23424 
deer 2131 213132
bear 2313 21313

Wariant Perla ze wzorem w pliku:

$ cat pattern.txt 
dog 123 4335
$ perl -ne 'BEGIN{chomp($p=(<STDIN>)[0])};print unless 1../$p/;' animals.txt < pattern.txt
cat 13123 23424 
deer 2131 213132
bear 2313 21313

2

Wth ed:

ed -s file1 <<< '/dog 123 4335/+1,$p'

To wysyła jedno ppolecenie rint do ed w ciągu tutaj; polecenie drukowania jest ograniczone w zakresie do jednego po ( +1) dog 123 4335dopasowaniu do końca pliku ( $).


1

Jeśli nie przeszkadza ci utworzenie pliku tymczasowego i masz go do csplitdyspozycji, działa to:

sh -c 'csplit -sf"$1_" "$1" "%^$(cat "$2")%+1" && cat "${1}_00"' sh file1 file2

Uwaga file1jest plikiem wejściowym i plikiem file2wzoru (jak podano w pytaniu).

Długa postać powyższego polecenia to:

sh -c 'csplit --quiet --prefix="$1_" "$1" "%^$(cat "$2")%+1" && cat "${1}_00"' sh file1 file2

to znaczy,

csplit --quiet --prefix="file1_" "file1" "%^$(cat "file2")%+1" && cat "file1_00"

csplitbez prefixpowyższej flagi utworzyłby plik xx00(prefiks xxi sufiks 00). Z flagą powyżej tworzy plik file1_00. Bez quietflagi drukuje rozmiar pliku wyjściowego (rozmiar pliku wynikowego).


0

Ponieważ awk nie jest wyraźnie niedozwolone, oto moja oferta, zakładając, że „kot” jest dopasowany.

awk '$0 ~ /cat/ { vart = NR }{ arr[NR]=$0 } END { for (i = vart; i<=NR ; i++) print arr[i]  }' animals.txt

0

Jak wydrukować wszystkie linie po dopasowaniu do końca pliku?

Innym sposobem na określenie tego jest „jak usunąć wszystkie wiersze z pierwszego do dopasowania (w tym)”, i można to sednapisać jako:

sed -e '1,/MATCH PATTERN/d'

1
Jedynym problemem jest to, że wzór znajduje się w pierwszej linii ...
don_crissti


Wydaje mi się, że potrzebujemy tutaj komisji do podjęcia decyzji.
poige

1
@poige: nie, podajesz tę samą odpowiedź mniej wyczerpująco
Thor

@don_crissti, a co sed -e '0,/MATCH PATTERN/d'wtedy?
Velkan,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.