Jak używać seda do usunięcia ostatnich n wierszy pliku


148

Chcę usunąć kilka n wierszy z końca pliku. Czy można to zrobić za pomocą seda?

Na przykład, aby usunąć linie od 2 do 4, mogę użyć

$ sed '2,4d' file

Ale nie znam numerów linii. Mogę usunąć ostatnią linię za pomocą

$sed $d file

ale chcę wiedzieć, jak usunąć n wierszy z końca. Daj mi znać, jak to zrobić używając seda lub innej metody.


2
@arashkordi: używa liczby wierszy od początku pliku, a nie od dołu.
AMS

Powiązane pytanie dotyczące superużytkowników .
Thor

//, Może rozważyć przeformułowanie pytania, aby było bardziej ogólne niż tylko sed ?
Nathan Basanese,

1
sed $d filezwraca błąd. Zamiast tego $ d powinno znajdować się w cudzysłowie, na przykład:sed '$d' file
Akronix,

Odpowiedzi:


221

Nie wiem sed, ale można to zrobić za pomocą head:

head -n -2 myfile.txt

19
+1 za prostotę. Równoważne polecenie sed jest brzydkie: sed -e :a -e '$d;N;2,5ba' -e 'P;D' file( ,5dla ostatnich 5 wierszy).
Marc B

62
Zauważ, że działa to z niektórymi wersjami programu head, ale nie jest standardowe. Rzeczywiście, standard dla headstanów:The application shall ensure that the number option-argument is a positive decimal integer.
William Pursell

21
Aby dodać do odpowiedzi @ WilliamPursell ... W domyślnej powłoce Mac OS to nie działa.
JDS

7
Ani na BSD, ale pytanie jest oznaczone jako Linux.
rano

3
minus 1, ponieważ oprócz osx, to nie działa dla mnie na Ubuntu14 -head: illegal line count -- -2
Michael Durrant

30

Jeśli twarde kodowanie n jest opcją, możesz użyć sekwencyjnych wywołań seda. Na przykład, aby usunąć ostatnie trzy wiersze, trzykrotnie usuń ostatni wiersz:

sed '$d' file | sed '$d' | sed '$d'

2
To, plus -i do edycji pliku w miejscu zamiast zrzucania go na standardowe wyjście, działało dla mnie.
WAF

27

Z sedesów :

# delete the last 10 lines of a file
sed -e :a -e '$d;N;2,10ba' -e 'P;D'   # method 1
sed -n -e :a -e '1,10!{P;N;D;};N;ba'  # method 2

Wydaje się, że to jest to, za czym tęsknisz.


@Thor możesz zmienić ile linii jest usuwanych z pliku zmieniając 10in'$d;N;2,10ba'
Alexej Magura

1
@AlexejMagura: Komentowałem wcześniejszą wersję odpowiedzi.
Thor

22

Zabawne i proste sedi tacrozwiązanie:

n=4
tac file.txt | sed "1,$n{d}" | tac

UWAGA

  • "Aby powłoka obliczyła $nzmienną w sedpoleceniu, potrzebne są podwójne cudzysłowy . W pojedynczych cudzysłowach nie zostanie wykonana żadna interpolacja.
  • tacjest catodwrócona, widzicieman 1 tac
  • {}w sedsą tam, aby oddzielić $ni d(jeśli nie, spróbuj powłoki interpolować non istniejącej $ndzmiennej)

7
fyi no tacna osx
Michael Durrant

1
@MichaelDurrant port info coreutils|| brew info coreutils;
głosy

19

Użyj sed, ale pozwól powłoce wykonać obliczenia, mając na celu użycie dpolecenia przez podanie zakresu (aby usunąć ostatnie 23 wiersze):

sed -i "$(($(wc -l < file)-22)),\$d" file

Aby usunąć ostatnie 3 wiersze, od wewnątrz:

$(wc -l < file)

Podaje liczbę wierszy pliku: powiedzmy 2196

Chcemy usunąć ostatnie 23 wiersze, więc dla lewej strony lub zakresu:

$((2196-22))

Daje: 2174 Tak więc oryginalny sed po interpretacji powłoki to:

sed -i '2174,$d' file

Dzięki -iedycji w miejscu plik ma teraz 2173 wierszy!

Jeśli chcesz zapisać go w nowym pliku, kod to:

sed -i '2174,$d' file > outputfile

15

Możesz użyć do tego głowy .

Posługiwać się

$ head --lines=-N file > new_file

gdzie N to liczba wierszy, które chcesz usunąć z pliku.

Zawartość oryginalnego pliku bez ostatnich N linii znajduje się teraz w new_file


8

Dla kompletności chciałbym dodać moje rozwiązanie. Skończyło się na tym, że zrobiłem to ze standardem ed:

ed -s sometextfile <<< $'-2,$d\nwq'

To usunie ostatnie 2 linie za pomocą edycji w miejscu (choć nie użyć pliku tymczasowego w /tmp!!)


5

Aby naprawdę obciąć bardzo duże pliki, mamy truncatepolecenie. Nie wie o liniach, ale tail+ wcpotrafi konwertować linie na bajty:

file=bigone.log
lines=3
truncate -s -$(tail -$lines $file | wc -c) $file

Jeśli plik jest zapisywany w tym samym czasie, istnieje oczywisty wyścig. W tym przypadku może być lepiej użyć head- liczy bajty od początku pliku (pamiętaj o IO dysku), więc zawsze będziemy obcinać granicę linii (prawdopodobnie więcej linii niż oczekiwano, jeśli plik jest aktywnie zapisywany):

truncate -s $(head -n -$lines $file | wc -c) $file

Poręczna jedna linijka, jeśli nie uda Ci się zalogować, podając hasło zamiast nazwy użytkownika:

truncate -s $(head -n -5 /var/log/secure | wc -c) /var/log/secure

4

To może zadziałać dla Ciebie (GNU sed):

sed ':a;$!N;1,4ba;P;$d;D' file

Miły. Przegapiłeś kończący apostrof ( ').
Thor

przepraszam za tak późny komentarz, ale próbuję i (dostosowany do mojego systemu AIX / posix) nie udało się wydrukować całej linii oprócz ostatniej. Czytając kod, nie rozumiem, w jaki sposób ostatnie cztery wiersze są usuwane, jawna pętla znajduje się w 4 pierwszych wierszach, więc z pewnością zapętla się po a d, Dlub Pz GNU sed, a nie w wersji posix. Wydaje się, że wiersze nie są drukowane po znaku Di pozostają w roboczym buforze dla nowej pętli bez przechodzenia do 'końca' linii akcji.
NeronLeVelu

@NeronLeVelu program czyta w oknie 4 wiersze w przestrzeni wzorców, a następnie dołącza następną linię i drukuje pierwszą, aż osiągnie koniec pliku, w którym usuwa pozostałe linie.
potong

@potong tak, testuję na sedzie GNU i utrzymuję bufor między wierszami, gdzie posix wyładowuje go po Dwykonaniu odwrotnego efektu.
NeronLeVelu

4

Większość z powyższych odpowiedzi wydaje się wymagać poleceń / rozszerzeń GNU:

    $ head -n -2 myfile.txt
    -2: Badly formed number

Aby uzyskać nieco bardziej przenośne rozwiązanie:

     perl -ne 'push(@fifo,$_);print shift(@fifo) if @fifo > 10;'

LUB

     perl -ne 'push(@buf,$_);END{print @buf[0 ... $#buf-10]}'

LUB

     awk '{buf[NR-1]=$0;}END{ for ( i=0; i < (NR-10); i++){ print buf[i];} }'

Gdzie „10” to „n”.


2

Dzięki odpowiedziom tutaj dowiedziałeś się już, że sed nie jest najlepszym narzędziem do tej aplikacji.

Jednak myślę, że jest na to sposób, używając seda; pomysł polega na dodaniu N linii, aby zachować spację, dopóki nie będziesz w stanie czytać bez naciskania EOF. Po trafieniu EOF wypisuje zawartość miejsca wstrzymania i zakończ.

sed -e '$!{N;N;N;N;N;N;H;}' -e x

Powyższe polecenie sed pominie ostatnie 5 wierszy.


2

Wypróbuj następujące polecenie:

n = line number
tail -r file_name | sed '1,nd' | tail -r

Proszę wyjaśnić, jak to działa. FYI - aby zainicjalizować zmienne powłoki, nie powinniśmy mieć dookoła spacji =.
codeforester

@codeforester Pierwsza linia to chyba komentarz. Po prostu używa miejsca, w tail -rktórym inni używali tacdo odwrócenia kolejności linii i sprawienia, że ​​ostatnie nlinie staną się npierwszymi.
Nicolas Melay,

2

Można to zrobić w 3 krokach:

a) Policz liczbę wierszy w pliku, który chcesz edytować:

 n=`cat myfile |wc -l`

b) Odejmij od tej liczby liczbę linii do usunięcia:

 x=$((n-3))

c) Powiedz sedowi, aby usunął od tego numeru linii ( $x) do końca:

 sed "$x,\$d" myfile

Zła matematyka. Jeśli musisz usunąć 3 linie, odejmij 3, ale DODAJ 1. Za pomocą matematyki, jeśli plik ma 10 linii, 10 - 3 = 7 i używasz seddo usuwania linii od 7 do 10, czyli 4 linie, a nie 3.
mathguy

1

Możesz uzyskać całkowitą liczbę linii wc -l <file>i użyć

head -n <total lines - lines to remove> <file>


1

Spowoduje to usunięcie ostatnich 3 wierszy z file:

for i in $(seq 1 3); do sed -i '$d' file; done;


1
Jest to zbyt kosztowne dla dużych plików - cały plik musi zostać odczytany i przepisany n razy! I tyle widelców sed.
codeforester

chociaż może to nie być najbardziej wydajne rozwiązanie, jest zdecydowanie najprostsze i najłatwiejsze do zrozumienia
Dharam

0

Wolę to rozwiązanie;

head -$(gcalctool -s $(cat file | wc -l)-N) file

gdzie N to liczba linii do usunięcia.



0

Aby usunąć ostatnie 4 wiersze:

$ nl -b a file | sort -k1,1nr | sed '1, 4 d' | sort -k1,1n | sed 's/^ *[0-9]*\t//'   

trochę ciężki (szczególnie w zasobach) w porównaniu do głowy. Przynajmniej tac file | sed '1,4d' | tacdlatego, że sortowanie, a następnie usuwanie prefiksu kosztuje dużo zasobów.
NeronLeVelu

@NeronLeVelu masz rację, ale nie ma tacpolecenia w niektórych systemach (na przykład FreeBSD?)
mstafreshi

0

Wymyśliłem to, gdzie n to liczba linii, które chcesz usunąć:

count=`wc -l file`
lines=`expr "$count" - n`
head -n "$lines" file > temp.txt
mv temp.txt file
rm -f temp.txt

To małe rondo, ale myślę, że łatwo jechać.

  1. Policz liczbę wierszy w pliku głównym
  2. Odejmij liczbę wierszy, które chcesz usunąć z liczby
  3. Wydrukuj liczbę wierszy, które chcesz zachować i zapisz w pliku tymczasowym
  4. Zastąp plik główny plikiem tymczasowym
  5. Usuń plik tymczasowy

0

Aby usunąć ostatnie N wierszy pliku, możesz użyć tej samej koncepcji

$ sed '2,4d' file

Możesz użyć kombi z poleceniem tail, aby odwrócić plik: jeśli N to 5

$ tail -r file | sed '1,5d' file | tail -r > file

I w ten sposób działa również tam, gdzie head -n -5 filepolecenie nie działa (jak na Macu!).


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.