Jak usunąć co drugą linię z pliku?

25

Plik:

Data inserted into table. Total count 13
No error occurred
Data inserted into table. Total count 45
No error occurred
Data inserted into table. Total count 14
No error occurred
Data inserted into table. Total count 90
No error occurred

Oczekiwany plik wyjściowy:

Data inserted into table. Total count 13
Data inserted into table. Total count 45
Data inserted into table. Total count 14
Data inserted into table. Total count 90

Chcę, aby wynik wyglądał w ten sposób: co druga linia zostanie usunięta, ale nie będzie przerwy między wierszami.

pmaipmui
źródło
5
Czy chcesz usunąć co drugą linię lub wszystkie linie zawierające „Nie wystąpił błąd” ? Co jeśli w dwóch kolejnych wierszach pojawił się komunikat „Nie wystąpił błąd” ?
Tulains Córdova,
1
@ user1598390 Myślę, że ... w takim przypadku grep -v "No error occurred" fileto polecenie powinno działać ... na co odpowiedział @paul. W pliku wyjściowym nie będzie linii zawierających „Nie wystąpił błąd” tej części.
pmaipmui
1
Zatem tytuł pytania wprowadza w błąd.
Tulains Córdova,

Odpowiedzi:

36

Z sed:

sed -e n\;d <file

Z POSIX awk:

awk 'FNR%2' <file

Jeśli masz starsze awk(jak oawk), potrzebujesz:

oawk 'NR%2 == 1' <file

Z ex:

$ ex file <<\EX
:g/$/+d
:wq!
EX

edytuje plik na miejscu.

  • g zaznacz globalne polecenie
  • /$/ dopasuj wszystkie linie
  • +d usuń następny wiersz
  • wq! Zapisz wszystkie zmiany

To podejście łączy ten sam ideał z sedpodejściem, usuwaj każdą kolejną linię bieżącego początku linii od linii 1.

Z perl:

perl -ne 'print if $. % 2' <file

i perl6:

perl6 -ne '.say if $*IN.ins % 2' <file
perl6 -ne '.say if ++$ % 2' <file
Cuonglm
źródło
Tak ... jego pracy ... :) ... Pierwszy z nich działa .... Próbowałem również ten drugi .. jego wymowny `awk: syntax error line1 awk: ratowanie pobliżu linii 1'
pmaipmui
sed -en \; d <plik ~ Tak, działa @cuonglm ...
pmaipmui
1
Zgaduję, że użyłeś n\;dzamiast 'n;d'ocalić cenną postać, ale ta logika wychodzi z okna, gdy niepotrzebnie używasz -eprzełącznika i przekierowania pliku <!
Tom Fenech
1
@Geek: To tylko krótsza wersja sed -e 'n;d', ocalić jedną postać.
cuonglm
1
@Geek: npolecenie zapisz przestrzeń wzorców na standardowe wyjście, jeśli -nzostała użyta, a następnie zamień przestrzeń wzorców na następny wiersz. Tutaj każda nieparzysta linia zostanie wydrukowana przez n, parzysta linia, a następnie dwczytana do przestrzeni wzorów, ale natychmiast usunięta za pomocą polecenia`.
cuonglm
62

Rozwiązanie tego przez usunięcie co drugiej linii może być podatne na błędy (na przykład, gdy proces czasami generuje dwie znaczące linie zamiast jednej). Być może lepiej jest odfiltrować śmieci:

grep -v "No error occurred" file

Może działać jako filtr, możesz tutaj dodać więcej wzorców śmieci i poprawić wynik.

Paweł
źródło
9
+1 za wskazanie, że czasami druga linia jest ważna!
Kaz Wolfe
12

Odpowiadając na pytanie, z GNU sed:

sed '0~2d' file

usunie co drugi wiersz, ale chciałbym zaoferować filtrowanie wierszy według zawartości:

sed '/Data/! d' file

lub z tym samym wynikiem

sed '/No error/d' file
Costas
źródło
plik sed '/ No error / d' ~ daje pożądany wynik @Casas
pmaipmui
5
Zauważ, że dwa ostatnie to skomplikowane sposoby pisania grep Dataigrep -v 'No error'
Stéphane Chazelas
5

Oto sposób użycia sed:

sed -n 'p;n' filename

Inny sposób z GNU sed:

sed -n '1~2p' filename

Dane wyjściowe z powyższych poleceń:

Data inserted into table. Total count 13
Data inserted into table. Total count 45
Data inserted into table. Total count 14
Data inserted into table. Total count 90
serenesat
źródło
Co masz na myśli mówiąc shortest way using sed?
cuonglm
Jaki jest powód gdowodzenia? sed -n 'p;n'wystarczy.
Costas
@cuonglm: Mam na myśli prosty sposób robienia. Przy okazji usunąłem to słowo. :)
serenesat
@Costas: Dzięki! Właśnie sprawdziłem, działa bez g. usunięto g z polecenia. :)
serenesat
4

Możesz spróbować z awk:

awk 'NR % 2 != 0' file

lub możesz wydrukować tylko linie zawierające Data inserted:

awk '$0 ~ /Data inserted/' file
taliezin
źródło
Próbowałem obu odpowiedzi i oba działają ... :)
pmaipmui
3

Inna odpowiedź, możesz użyć vi / vim!

qdjddq

A jeśli twój plik miał na przykład 500 linii (wpisz)

250 @ d

A potem napisać i wyjść

: x

Lub jeśli coś pójdzie nie tak i nie chcesz oszczędzać:

: q!

Wyjaśnienie:

q      #Start Recording
 d     #Put the recording into register 'd'
  j    #Move the cursor down
   dd  #Delete the line
     q #Stop recording


250    #Number of repeats
   @d  #Playback the recording in register 'd'.
DJMcMayhem
źródło
2

Oto całkiem inny sposób:

< file paste - - | cut -f1

Zakłada się, że linie nieparzyste nie zawierają tabulatorów. Jeśli tak, musisz wybrać inny znak separatora, np. :Tutaj:

< file paste -d: - - | cut -d: -f1
Cyfrowa trauma
źródło
1
Miałem to na uwadze, kiedy po raz pierwszy zobaczyłem pytanie ... Interesujące byłoby przeprowadzenie testu prędkości sedz ogromnym plikiem (np. 20 mil linii). W każdym razie +1, ale tak naprawdę, aby uniknąć bólów głowy, wybierz ogranicznik, który prawdopodobnie nie wystąpi w pliku tekstowym, na przykład $'\002'...
don_crissti
@don_crissti tak użycie znaku niedrukowalnego dla separatora jest dobrym pomysłem. I tak, jest to wymiernie szybsze niż rozwiązanie sed. Utworzyłem plik testowy z seq 100000000 > 100mil.txt. paste|cutRoztwór zakończone w ciągu około 7,5 sekund, VS prawie 12 na sedroztwór. Wydaje się być powtarzalne. grepjest jednak najszybszy. Ubuntu 14.04 ze standardowymi narzędziami GNU.
Digital Trauma
Tak, paste+ cutsą mocno zoptymalizowani do swojej pracy, więc nic dziwnego, że ich kombinacja jest cholernie szybka ...
don_crissti
1

Inna opcja (krótsza)

sed 'n; d' file
Michael Durrant
źródło
3
Jest dłuższy niż mój sed n\;d, dodawanie -eto tylko mój nawyk.
cuonglm
0

Rozwiązuje również problem, choć jest nieco wolniejszy:

vim -c "%normal jdd" -c "wq" file
FloriOn
źródło