Grep usuwa linię z 0, ale nie 0,2?


12

Mam plik, którego treść jest podobna do poniższej.

0
0
0.2
0
0
0
0

Muszę usunąć wszystkie linie za pomocą jednego zera.
Myślałem o użyciu grep -v "0", ale usuwa to również wiersz zawierający 0.2. Widziałem, że mogę skorzystać z tej -wopcji, ale to też nie działa.

Jak mogę usunąć wszystkie linie zawierające tylko jedno 0 i zachować wszystkie te linie zaczynające się od 0?



1
@JulienLopez To nie jest dupek tego pytania. To pytanie dotyczy dopasowania słowa i odpowiedzi -w, co tutaj się nie udaje.
Sparhawk

Dlaczego jesteś zmuszony używać greptego zadania? A co dokładnie rozumiesz przez pojedyncze zero ? To brzmi bardzo podobnie do problemu XY .
Roland Illig

1
@RolandIllig było 1 godzinę przed snem i chciałem zacząć przetwarzać serię 500 000 ciągów znaków, aby sprawdzić, czy są to klucze prywatne bitcoin, a jeśli tak, to uzyskać równowagę. Następnym razem, kiedy miałem na to czas, przetworzyłem wiele tysięcy ciągów i chciałem po prostu przeanalizować wszelkie niezerowe wartości.
Philip Kirkbride

Odpowiedzi:


35
grep -vx 0

Od man grep:

-x, --line-regexp
       Select only those matches that exactly match the whole line.
       For a regular expression pattern, this is like parenthesizing
       the pattern and then surrounding it with ^ and $.

-wnie dlatego, że pierwszy 0w 0.02uważany jest za „słowo”, a więc ta linia jest dopasowana. Wynika to z faktu, że po nim następuje znak „inny niż słowo”. Możesz to zobaczyć, jeśli uruchomisz oryginalne polecenie bez -v, tj grep -w "0".


Możesz również skorzystać z tej -Fopcji, ponieważ nie używamy wzorców
wyrażeń

@glennjackman Być może przeczytałem to wcześniej, ale nie mogę tego teraz znaleźć. -FWydaje mi się, że bieganie (co jest dla mnie zaskakujące) zajmuje podobny czas lub nawet nieco wolniej (~ 5–10%). Dlatego nie jestem pewien, jaka byłaby korzyść.
Sparhawk

2
Możliwe jest, że silnik RegEx jest używany tak często i tak szeroko, że zaimplementowano jego bardzo wydajną wersję, ale „zwykłe wyszukiwanie” prawdopodobnie nie było uaktualniane od 30 lat.
Nelson

@Sparhawk: grepprzypuszczalnie ma specjalny przypadek wyrażeń regularnych bez metaznaków, ponieważ jest to częsty przypadek użycia. Zaskakujące, że fgrepbyłoby wolniejsze, ale nie jest zaskakujące, że narzut związany z zauważeniem tego szczególnego przypadku podczas kompilacji krótkiego wzorca jest pomijalny w porównaniu z czasem skanowania dużego pliku. (Jeśli wymaga to specjalnego przypadku, aby przejść tak szybko, a wzór z klasą postaci lub x.*y.)
Peter Cordes

Ale może to nadmierne uproszczenie, ponieważ dane wejściowe to tak naprawdę wiele krótkich linii (nie jeden gigantyczny ciąg). Zapominam, czy greprozpoznaje znak inny niż znak \nnowej linii jako separator linii. Jeśli nie, niejawne ^i $ nadal może przekształcić się w wyszukiwanie o stałym ciągu, takie jak strstr(big_buf, "\n0\n"). (Lub 0\nna początku bufora.) Ale nie szukamy tylko pierwszego dopasowania potencjalnie daleko w dużym buforze, chcemy skutecznie filtrować. Ale tak czy inaczej, teoretycznie tak, to tylko 2-bajtowe memcmp na początku każdej linii, i można mieć nadzieję, że zarówno fgrep, jak i grep to zobaczą.
Peter Cordes,

28

Z grep:

grep -v "^0$" file

^oznacza początek linii, $oznacza koniec linii.


2
O to prosił użytkownik: unikaj wierszy zawierających tylko 1 „0”.
Olivier Dulac

1
Nie umieszczałbym dosłownego znaku dolara w takich podwójnych cudzysłowach.
user541686,

@mehrdad nie jest wielkim problemem z wyrażeniem regularnym, ponieważ zwykle jest to albo ostatni znak, albo następny[a-Z0-9]
Sampo Sarrala - codidact.org

14

Chociaż grep można do tego użyć (jak wyraźnie pokazują inne odpowiedzi), cofnijmy się i zastanówmy się, czego naprawdę chcesz:

  • Masz plik zawierający liczby
  • Chcesz przeprowadzić filtrowanie na podstawie wartości liczbowej .

Regex interpretuje dane sekwencji znaków. Nie wiedzą o liczbach, tylko o poszczególnych cyfrach (i ich regularnych kombinacjach). Chociaż w twoim konkretnym przypadku istnieje prosty hack wokół tego ograniczenia, ostatecznie jest to niedopasowanie wymagań.

O ile nie ma tutaj bardzo dobrego powodu grep(np. Ponieważ zmierzyłeś go, a jest on znacznie bardziej wydajny, a wydajność jest kluczowa w twoim przypadku), zalecam użycie innego narzędzia.

awk, na przykład, może filtrować na podstawie porównań numerycznych, np .:

awk '$1 == 0' your_file

Ale także, aby uzyskać wszystkie wiersze zawierające liczby większe niż zero:

awk '$1 > 0' your_file

Uwielbiam wyrażenia regularne, to świetne narzędzie. Ale to nie jedyne narzędzie. Jak to się mówi, jeśli wszystko, co masz, to grepwszystko wygląda jak zwykły język.


3
Z całego serca zgadzam się, że awk może być tutaj bardziej elegancki ... będzie jednak pasował może nieco więcej niż oczekuje użytkownik (każda wartość liczbowa równa 0). Czyli printf '0\n1\n-1\na\nb\n0\n0 also\n0.0\n-0.0\n0*0\n' | awk '($1 == 0)'będzie pasował: 0, 0.0i -0.0... i też 0 also! Nie tylko „0”. (co czasem jest potrzebne, a czasem nie). Jeśli użytkownik chce tylko „0”: awk '/^0$/' (lub grep '^0$'). Powinieneś także edytować: użytkownik musi dodać, !aby negować test, więc ukrywa 0(i inne zera) i wyświetla resztę. tj .:awk '!( $0 == 0)'
Olivier Dulac

1
@Olivier lub sprawdź wartość ciągu:$1 == "0"
glenn jackman

1
@OlivierDulac Użyłem jawnie >zamiast !=(lub równoważnie ! (… == …)), aby podkreślić, że jest to dowolne porównanie numeryczne, a nie tylko równość. Co do twojego innego komentarza, jest to w pełni prawdą, ale zasadniczo wróciliśmy do terytorium porównania ciągów i istniejącego rozwiązania wykorzystującego grepdzieła (choć awkoczywiście również działa).
Konrad Rudolph

@KonradRudolph uczciwe punkty :)
Olivier Dulac

1
@glennjackman: naprawdę fajna sztuczka. Ale wtedy OP wolałby zrobić test$0=="0"
Olivier Dulac

5

grep„s -wjest nieco zawiłe w taki sposób, że dzieli się oryginalny łańcuch do słowa i non-słownych składników (nic oprócz liter, cyfr lub podkreślenie). Ponieważ już napotkał aa ważny składnik słowo 0w 0.02to nie zapewnił logikę negacji, aby usunąć linię.

Używanie sedjest w tym kontekście nieco łatwe, aby po prostu usunąć całe pasujące słowa

sed '/^0$/d' file

3

Kiedy linie chcesz usunąć tylko zawierać 0 następuje następnego wiersza można wybrać te linie, wydając następującą komendę:

grep -v "^0$"

Spowoduje to wydrukowanie tylko tych wystąpień, 0które znajdują się na końcu linii i na początku linii w tym samym czasie. -vOpcja następnie odwraca naszą ofertę.


1
Ta odpowiedź jest prawie identyczna z odpowiedzią Arkadiusza Drabczyka, ale zapomniałeś -v, więc nie działa.
Sparhawk

Masz rację. Pisałem na maszynie, kiedy opublikował swoją odpowiedź, więc nie widziałem, że została już podana. Źle odczytałem tę część -vopcji, dzięki!
majesticLSD

0
  • \ b - obramowanie słów

grep -v "\b0\b"

  • dopasuj początek linii, wzór i koniec linii

grep -v "^0$"

  • lub jak sugerował @Sparhawk -vx lineregexp

-w działa, ale w twoim przypadku 0,2 to dwa słowa, ponieważ znak kropki jest separatorem słów.


grep -v "\b0\b"tak naprawdę tu nie działa. Jakiej wersji grep używasz?
Arkadiusz Drabczyk

współpracuje z grep (BSD grep) 2.5.1-FreeBSDMacOS i grep (GNU grep) 2.16Ubuntu
Jakub Jindra

1
Wykorzystanie wyrażenia regularnego GNU \<i \>jako granice słów, ale będzie to miało taki sam efekt jak-w
glenn jackman

0

Kolejna odpowiedź ze względu na różnorodność, przy założeniu, że masz włączoną obsługę PCRE grep

grep -Pv "^0(?!\.)"

powoduje to negatywne spojrzenie w przód, aby dopasować do linii zaczynających się od 0i po których nie następuje kropka. Następnie -vodrzuca niepasujące linie. Można zobaczyć w akcji tutaj


1
Spowoduje to również usunięcie wierszy takich jak 0123, czego nie chce OP
iruvar

0

Zakładając, że każdy wiersz, który nie jest pojedynczym 0, ma kropkę

grep '\.' file

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.