Naprzeciwko ogona: wszystkie linie oprócz ostatnich n linii

36

Jak mogę odrzucić ostatnie n wierszy pliku z uniksowym filtrem wiersza poleceń?

To byłoby w pewnym sensie przeciwne tail: tailodrzuca pierwsze n linii, ale przepuszcza resztę, ale chcę, aby polecenie przepuściło wszystko oprócz ostatnich n linii.

Niestety nie znalazłem czegoś takiego - też headnie pomaga. EDYCJA : Przynajmniej w Solarisie nie przyjmuje negatywnych argumentów.

Aktualizacja: Najbardziej interesuje mnie rozwiązanie, które działa dla dużych plików, tj. Logów, gdzie możesz chcieć sprawdzić, co się stało w ostatnich minutach.

Hans-Peter Störr
źródło
Do Twojej wiadomości przy użyciu head: Umieszczając „-” przed liczbą z opcją -n, drukuje wszystkie linie każdego pliku, ale nie ostatnie N linii, jak pokazano poniżej,
G Koe

Odpowiedzi:

38

Jeśli masz GNU head, możesz użyć

head -n -5 file.txt

wydrukować wszystkie oprócz ostatnich 5 wierszy file.txt.

Jeśli head -nnie przyjmuje żadnych negatywnych argumentów, spróbuj

head -n $(( $(wc -l file.txt | awk '{print $1}') - 5 )) file.txt
użytkownik570500
źródło
11
(i módlcie się, żeby miał file.txtco najmniej sześć linii ...)
CV
1
Niestety, ta wersja inna niż GNU również nie działa ze strumieniami
Armand
1
@ MichaelKjörling Przynajmniej na Ubuntu, to nie jest problem. Jeśli pliki zawierają mniej wierszy niż określono w head, zwracane jest puste wyjście bez błędów.
Alphaaa,
Jeśli się nie mylę, głowa -n 5 wydrukuje pierwsze 5 wierszy, nie wszystkie oprócz ostatnich 5 ...
pypmannetjies
8
head file.txt               # first 10 lines
tail file.txt               # last 10 lines
head -n 20 file.txt         # first 20 lines
tail -n 20 file.txt         # last 20 lines
head -20 file.txt           # first 20 lines
tail -20 file.txt           # last 20 lines
head -n -5 file.txt         # all lines except the 5 last
tail -n +5 file.txt         # all lines except the 4 first, starts at line 5
Kjetil S.
źródło
1
Co to jest dodanie, na które nie udzielono odpowiedzi w zaakceptowanej odpowiedzi? Podobnie jak w przypadku innych odpowiedzi, kilka linii wyjaśnienia Twojej odpowiedzi znacznie ją poprawi.
music2myear
1
bardzo dobre podsumowanie
ruanhao
5

Oto prosty sposób na usunięcie ostatniego wiersza, który działa na BSD itp.

sed '$d' input.txt

Wyrażenie brzmi „w ostatnim wierszu, usuń go”. Pozostałe linie zostaną wydrukowane, ponieważ jest sedto domyślne zachowanie.

Możesz połączyć je razem, aby usunąć wiele linii

sed '$d' input.txt | sed '$d' | sed '$d'

Co jest trochę ciężkie, co prawda, ale wykonuje tylko jedno skanowanie pliku.

Możesz również na to spojrzeć, aby uzyskać więcej odpowiedzi: https://stackoverflow.com/questions/13380607/how-to-use-sed-to-remove-last-n-lines-of-a-file

Oto linijka dostosowana do jednego z moich ulubionych:

N=10
sed -n -e ':a' -e "1,$N!{P;N;D;};N;ba"

Miałem frajdę z rozszyfrowania tego i mam nadzieję, że ty również (: NSkanuje linie buforowe , ale poza tym jest całkiem wydajny.

jwd
źródło
3

Jestem ciekawy, dlaczego według ciebie headnie ma takiej opcji:

~$ man head
...
-n, --lines=[-]K
        print the first K lines instead of the first 10; 
        with the leading `-', print all but the last K lines of each file

Wydaje się to pasować do twojego celu, na przykład używając:

head -n -20 yourfile.txt
Anders R. Bystrup
źródło
5
Zauważ, że dotyczy to tylko GNU head. BSD headnie ma tej opcji, więc ta odpowiedź nie będzie działać na Solarisie lub innych Uniksach bez jądra GNU. OP specjalnie oznaczył to tagami Unix i Unix-Utils.
slhck 30.01.2013
2
@slhck Nie wspominając już o tym, że OP wspomniało, że dotyczy to Solaris.
CVn
Niestety ktoś usunął moją wzmiankę o Solarisie. Ale i tak powinienem wspomnieć, że wersja head tego nie obsługuje.
Hans-Peter Störr,
1
Przepraszam wszystkich. Nie zauważyłem Solaris, ani nie wiedziałem o różnych wersjach głowy.
Anders R. Bystrup
1
@hstoerr Solaris jest teraz w twoich tagach :)
slhck 30.01.2013
0

Innym sposobem na zrobienie tego, jeśli tail -nnie przyjmie się argumentów negatywnych, jest

tac file.txt | tail -n +6 | tac

Spowoduje to usunięcie ostatnich 5 wierszy

atw31337
źródło
Dzięki! To świetny pomysł, na który nikt dotąd nie wpadł. Niestety byłoby to dość nieefektywne w przypadku użycia, o którym myślałem z tym pytaniem: jeśli jest to duży plik, nie tylko zostałby on przeczytany raz lub więcej razy, jak w przypadku innych rozwiązań, ale prawdopodobnie zostałby również zapisany w dysk do plików tymczasowych przez tac, jeśli nie mieści się w pamięci.
Hans-Peter Störr
@ Hans-Peter Bardzo prawda. Postanowiłem napisać dla niego skrypt python3. Spróbuj tego github.com/atw31337/donkey . Polecam korzystanie z opcji wyjściowych. Działają znacznie szybciej niż przekierowania.
atw31337
Ładnie napisane! Odczytuje jednak plik dwa razy, co nie jest tak naprawdę konieczne, jeśli buforujesz ostatnie n wierszy, a jest to problem w przypadku dużych plików. Osobiście już tego nie potrzebuję, ale na wypadek, gdybyście się dobrze bawili, poprawiając go, a inni tego potrzebują ... W końcu było kilka polubień i zakładek na pytanie.
Hans-Peter Störr
@ Hans-Peter. Rozmiar bufora byłby zależny od liczby linii do usunięcia. Może to być problem, jeśli z pliku trzeba usunąć bardzo dużą liczbę wierszy. Aby uniknąć problemów związanych z pamięcią, przepisałem skrypt, aby używał metody liczenia wierszy o wysokich wartościach n i metody buforowania o niższych wartościach n; jednak po przetestowaniu go na bardzo dużym pliku oryginalna metoda liczenia linii jest jeszcze szybsza. Wydaje się, że narzut związany z zarządzaniem buforem przeważa narzut na liczbę wierszy ... lub po prostu czegoś brakuje.
atw31337
Fajnie, ale dla Mac OS X w wariancie BSD domyślnie nie ma polecenia tac. :( Ten rodzaj pokonuje przypadek użycia.
ing