Usunąć określone numery wierszy z pliku tekstowego za pomocą sed?

235

Chcę usunąć jeden lub więcej określonych numerów linii z pliku. Jak mam to zrobić za pomocą sed?

Justin Ethier
źródło
1
Czy możesz podać bardziej konkretny przykład tego, czego chcesz? Jak zdecydujesz, które wiersze usunąć?
Mark Byers
Może zobacz także stackoverflow.com/questions/13272717/… i po prostu zastosuj w odwrotnej kolejności (wypisz, jeśli klucz nie jest w tablicy asocjacyjnej).
tripleee

Odpowiedzi:

374

Jeśli chcesz usunąć wiersze od 5 do 10 i 12:

sed -e '5,10d;12d' file

Spowoduje to wydrukowanie wyników na ekranie. Jeśli chcesz zapisać wyniki w tym samym pliku:

sed -i.bak -e '5,10d;12d' file

Spowoduje to utworzenie kopii zapasowej pliku file.baki usunięcie podanych wierszy.

Uwaga: Numery linii zaczynają się od 1. Pierwszy wiersz pliku to 1, a nie 0.

Brian Campbell
źródło
32
Nie wszystkie uniksy mają gnu sed z „-i”. Nie popełnij błędu, wracając do „sed cmd file> file”, który wymaże twój plik.
pra
4
co jeśli chciałbym usunąć 5. linię do ostatniej linii?
Jürgen Paul
14
@WearetheWorldsed -e '5,$d' file
Brian Campbell
1
@BrianCampbell Co powinienem zrobić, aby usunąć tylko określoną linię?
Kanagavelu Sugumar
14
@KanagaveluSugumar sed -e '5d' file. Składnia jest następująca <address><command>; gdzie <address>może być pojedyncza linia 5lub zakres linii podobnych 5,10, a polecenie dusuwa daną linię lub linie. Adresami mogą być również wyrażenia regularne lub znak dolara $wskazujący ostatnią linię pliku.
Brian Campbell
50

Możesz usunąć konkretny pojedynczy wiersz z jego numerem przez

sed -i '33d' file

Spowoduje to usunięcie wiersza z numerem 33 i zapisanie zaktualizowanego pliku.

amit
źródło
1
W moim przypadku „sed” usunął złą linię. Więc używam tego podejścia: sed -i '0,/<TARGET>/{/<NEW_VALUE>/d;}' '<SOME_FILE_NAME>'. Dzięki!
Eduardo Lucio
Tak samo napisałem pętlę i, co dziwne, niektóre pliki straciły prawidłową linię, ale niektóre pliki też straciły jedną linię, nie mają pojęcia, co poszło źle. (GNU / Linux bash4.2) Poniższe polecenie awk działało poprawnie w pętli
FatihSarigol
Zachowaj ostrożność, używając sort -r, jeśli usuwasz z listy linii, w przeciwnym razie twój pierwszy sed zmieni numery linii wszystkiego innego! ...
Konchog 30.10.18
Aby wypowiedzieć się na temat usuwania niewłaściwych wierszy w pętli: zacznij od największego numeru wiersza, w przeciwnym razie każdy usunięty wiersz
wyrówna
25

i awk również

awk 'NR!~/^(5|10|25)$/' file
ghostdog74
źródło
2
NB: Ta linia awk działała dla mnie bardziej niezawodnie niż wariant sed (między OS-X a Ubuntu Linux)
Jay Taylor
3
Pamiętaj, że to nie usuwa niczego z pliku. Po prostu drukuje plik bez tych linii na standardowe wyjście. Musisz także przekierować dane wyjściowe do pliku tymczasowego, a następnie przenieść plik tymczasowy, aby zastąpić oryginał.
mivk
17
$ cat foo
1
2
3
4
5
$ sed -e '2d;4d' foo
1
3
5
$ 
Matthew Slattery
źródło
6

Jest to bardzo często objaw antypatternu. Narzędzie, które utworzyło numery linii, może zostać zastąpione tym, które natychmiast usuwa linie. Na przykład;

grep -nh error logfile | cut -d: -f1 | deletelines logfile

(gdzie deletelinesjest narzędzie, którego wyobrażasz sobie potrzebujesz) jest takie samo jak

grep -v error logfile

Powiedziawszy to, jeśli jesteś w sytuacji, w której naprawdę musisz wykonać to zadanie, możesz wygenerować prosty sedskrypt z pliku numerów linii. Humorystycznie (ale być może nieco myląco) możesz to zrobić sed.

sed 's%$%d%' linenumbers

To akceptuje plik z numerami wierszy, po jednym w wierszu, i generuje na standardowym wyjściu te same numery wierszy z ddopisywanymi po każdym. To jest prawidłowy sedskrypt, który możemy zapisać do pliku lub (na niektórych platformach) potok do innej sedinstancji:

sed 's%$%d%' linenumbers | sed -f - logfile

Na niektórych platformach sed -fnie rozumie argumentu opcji -oznaczającego standardowe wejście, więc musisz przekierować skrypt do pliku tymczasowego i wyczyścić go, gdy skończysz, lub może zastąpić samotną kreskę /dev/stdinlub /proc/$pid/fd/1jeśli twój system operacyjny (lub powłokę) ) ma to.

Jak zawsze, możesz dodać -iprzed -fopcją sededycji pliku docelowego na miejscu, zamiast generować wynik na standardowym wyjściu. Na platformach * BSDish (w tym OSX) należy również podać jawny argument -i; powszechnym idiomem jest podawanie pustego argumentu; -i ''.

potrójny
źródło
Nie do końca zgadzam się z „objawem antypatternu”. Typy plików oparte na znacznikach (np. XML lub JSON) wymagają określonych linii na końcu, aby były poprawnymi plikami. W takim przypadku często jest to najbardziej rozsądne podejście do usunięcia tych linii, umieszczenia w pliku tego, co chcesz dodać, a następnie ponownego dodania tych linii, ponieważ umieszczenie linii między nimi może być znacznie większym wysiłkiem i jest sprzeczne z potencjalna chęć uniknięcia dodatkowych narzędzi, takich jak sed, na tyle, na ile możesz.
Egor Hans,
Nie do końca rozumiem, jaki scenariusz sobie wyobrażasz. Tam sytuacje, w których jest to uzasadnione podejście, ale zdecydowana większość przypadków widziałem są początkujących, którzy wykonują mniej lub bardziej dokładnie, co mój pierwszy przykład demonstruje. (Być może pochodzić z jakiegoś języka bardzo niskim poziomie i są wykorzystywane do podzielenia ich problem drogę obok poziomie molekularnym, bo trzeba w asm lub C)
tripleee
Usuwanie rzeczy według numeru wiersza z XML lub JSON wydaje się wyjątkowo kruche, jeśli nie wręcz niebezpieczne.
tripleee
Zasadniczo mam na myśli to, że jako twórca takiego pliku wiesz, co musi znajdować się na końcu dokumentu (tj. Zestaw nawiasów zamykających / nawiasów kwadratowych w ostatnich kilku wierszach dla JSON, lub dokładny zamykanie tagów dla XML). Mając to na uwadze, najprostszym podejściem do rozszerzenia takiego dokumentu jest 1) usunięcie kilku ostatnich wierszy, 2) dodanie nowej treści, 3) ponowne dodanie kilku ostatnich wierszy. W ten sposób dokument może być ważny zarówno przed rozszerzeniem, jak i po nim, bez konieczności szukania sposobu dodawania wierszy w środku dokumentu.
Egor Hans
1
Jak dotąd jest to jedyna odpowiedź z odpowiednim rozwiązaniem dla dużej liczby linii (tzn. Dostarczona przez plik). Przedmowa też ma sens. Zasługuje na więcej głosów pozytywnych. BTW, jeśli chcesz wydrukować linie zamiast je usuwać, użyj pzamiast d, wraz z opcją -n(nie będzie działać bez -ni !dnie będzie działać).
Skippy le Grand Gourou
2

Chciałbym zaproponować uogólnienie za pomocą awk.

Gdy plik jest tworzony przez bloki o ustalonym rozmiarze, a linie do usunięcia są powtarzane dla każdego bloku, awk może działać poprawnie w taki sposób

awk '{nl=((NR-1)%2000)+1; if ( (nl<714) || ((nl>1025)&&(nl<1029)) ) print  $0}'
 OriginFile.dat > MyOutputCuttedFile.dat

W tym przykładzie rozmiar bloku wynosi 2000 i chcę wydrukować linie [1..713] i [1026..1029].

  • NR to zmienna używana przez awk do przechowywania bieżącego numeru linii.
  • % podaje resztę (lub moduł) podziału dwóch liczb całkowitych;
  • nl=((NR-1)%BLOCKSIZE)+1Tutaj piszemy w zmiennej nl numer linii w bieżącym bloku. (patrz poniżej)
  • ||i &&są operatorem logicznym OR i AND .
  • print $0 pisze pełną linię

Why ((NR-1)%BLOCKSIZE)+1:
(NR-1) We need a shift of one because 1%3=1, 2%3=2, but 3%3=0.
  +1   We add again 1 because we want to restore the desired order.

+-----+------+----------+------------+
| NR  | NR%3 | (NR-1)%3 | (NR-1)%3+1 |
+-----+------+----------+------------+
|  1  |  1   |    0     |     1      |
|  2  |  2   |    1     |     2      |
|  3  |  0   |    2     |     3      |
|  4  |  1   |    0     |     1      |
+-----+------+----------+------------+

Hastur
źródło
2
Podziwiam sposób, w jaki zasługujesz na swoje imię wywołujące szaleństwo.
Jukka Dahlbom