Jak uzyskać odwrotne zachowanie dla „ogona” i „głowy”?

55

Czy istnieje sposób na head/ taildokument i uzyskanie odwrotnego wyniku; bo nie wiesz, ile wierszy jest w dokumencie?

Tzn. Chcę tylko uzyskać wszystko oprócz pierwszych 2 wierszy, foo.txtaby dołączyć do innego dokumentu.

chrisjlee
źródło

Odpowiedzi:

57

Możesz użyć tego do rozebrania pierwszych dwóch linii:

tail -n +3 foo.txt

i to, aby usunąć dwie ostatnie linie:

head -n -2 foo.txt

(zakładając, że plik kończy się \nna drugim)


Podobnie jak w przypadku standardowego użycia taili headte operacje nie są destrukcyjne. Użyj, >out.txtjeśli chcesz przekierować dane wyjściowe do nowego pliku:

tail -n +3 foo.txt >out.txt

W przypadku, gdy out.txtjuż istnieje, zastąpi ten plik. Użyj >>out.txtzamiast, >out.txtjeśli wolisz, aby wynik był dołączony out.txt.

Stéphane Gimenez
źródło
3
re „head, gdy plik kończy się na \n .. Działa dla wszystkich liczb całkowitych ujemnych innych niż te, -n -0które w ogóle nic nie zwracają , tak jak -n 0by to robił (używając: head (GNU coreutils) 7.4) ... Jednak gdy występuje trailing \n, -n -0drukuje jak można się spodziewać po -, tj. drukuje cały plik ... Tak to działa dla wszystkich nie-zerowych wartości ujemnych .. ale -0nie powiedzie się, gdy nie ma spływu\n
Peter.O
@fred: Dziwne, rzeczywiście… (to samo z 8.12 tutaj)
Stéphane Gimenez
Czy ta operacja jest destrukcyjna? Czy chcę skopiować odwrotność pierwszych dwóch wierszy dokumentu do innego?
chrisjlee
@Chris: Nie, po prostu drukują wynik na „standardowym wyjściu”, który zwykle jest podłączony do terminala. Dodałem kilka szczegółów, jak przekierować dane wyjściowe do niektórych plików.
Stéphane Gimenez
7
head -n -2nie jest kompatybilny z POSIX .
l0b0
9

Jeśli chcesz mieć wszystkie oprócz pierwszych linii N-1, zadzwoń tailz liczbą linii +N. (Liczba jest numerem pierwszego wiersza, który chcesz zachować, zaczynając od 1, tzn. +1 oznacza początek na górze, +2 oznacza pominięcie jednej linii i tak dalej).

tail -n +3 foo.txt >>other-document

Nie ma łatwego, przenośnego sposobu na pominięcie ostatnich N linii. GNU headpozwala head -n +Njako odpowiednik tail -n +N. W przeciwnym razie, jeśli masz tac(np. GNU lub Busybox), możesz połączyć go z tailem:

tac | tail -n +3 | tac

Przenośnie możesz użyć filtra awk (niesprawdzony):

awk -vskip=2 '{
    lines[NR] = $0;
    if (NR > skip) print lines[NR-skip];
    delete lines[NR-skip];
}'

Jeśli chcesz usunąć kilka ostatnich wierszy z dużego pliku, możesz określić przesunięcie bajtu kawałka do obcięcia, a następnie wykonać obcięcie za pomocą dd.

total=$(wc -c < /file/to/truncate)
chop=$(tail -n 42 /file/to/truncate | wc -c)
dd if=/dev/null of=/file/to/truncate seek=1 bs="$((total-chop))"

Na początku nie można obciąć pliku, ale jeśli chcesz usunąć kilka pierwszych wierszy dużego pliku, możesz przenosić zawartość .

Gilles „SO- przestań być zły”
źródło
W niektórych systemach (takich jak współczesny Linux), możesz obciąć (zwinąć) plik na miejscu, ale zwykle tylko o wielkość, która jest wielokrotnością wielkości bloku FS (więc w tym przypadku tak naprawdę nie jest przydatny).
Stéphane Chazelas
3

Ze strony podręcznika tail(czyli GNU tail):

-n, --lines=K
   output the last K lines, instead of the last 10; or use -n +K to
   output lines starting with the Kth

Dlatego poniższe powinny dołączyć wszystkie oprócz pierwszych 2 wierszy somefile.txtdo anotherfile.txt:

tail --lines=+3 somefile.txt >> anotherfile.txt
Steven Monday
źródło
3

Aby usunąć pierwsze n wierszy, można użyć GNU sed. Na przykład, jeśli n = 2

sed -n '1,2!p' input-file

!Średni „wykluczyć ten przedział”. Jak można sobie wyobrazić, można na przykład uzyskać bardziej skomplikowany wynik

sed -n '3,5p;7p'

pokaże linię 3,4,5,7. Większa moc pochodzi z używania wyrażeń regularnych zamiast adresów.

Ograniczeniem jest to, że numery linii muszą być znane z góry.

enzotib
źródło
1
Dlaczego nie tylko sed 1,2d? Prostsze jest zwykle lepsze. Również nic w twoich przykładach nie jest specyficzne dla GNU Sed; wszystkie twoje polecenia używają standardowych funkcji POSIX Sed .
Wildcard
1

Możesz użyć diffdo porównania danych wyjściowych head/ tailz oryginalnego pliku, a następnie usunąć to samo, uzyskując odwrotność.

diff --unchanged-group-format='' foo.txt <(head -2 foo.txt)
sokoban
źródło
1

Podczas tail -n +4gdy plik wyjściowy rozpoczynający się od czwartej linii (wszystkie oprócz pierwszych 3 linii) jest standardowy i przenośny, jego headodpowiednik ( head -n -3wszystkie oprócz ostatnich 3 linii) nie jest.

Przenośnie zrobiłbyś:

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

Lub:

sed -ne :1 -e '1,3{N;b1' -e '}' -e 'P;N;D'

(uwaga, że ​​w niektórych systemach, w których sedprzestrzeń wzorców ma ograniczony rozmiar, nie skaluje się do dużych wartości n).

Lub:

awk 'NR>3 {print l[NR%3]}; {l[NR%3]=$0}'
Stéphane Chazelas
źródło
1
{   head -n2 >/dev/null
    cat  >> other_document
}   <infile

Jeśli <infilejest to zwykły lseek()plik, który można obsługiwać, to tak, z całą pewnością, nie krępuj się. Powyższe jest konstrukcją w pełni obsługiwaną przez POSIXly.

mikeserv
źródło
0

Moje podejście jest podobne do Gillesa, ale zamiast tego po prostu odwracam plik poleceniem cat i pipe, używając polecenia head.

tac -r thefile.txt | head thisfile.txt (zastępuje pliki)

Abe
źródło
0

Mam nadzieję, że zrozumiałem twoją potrzebę.

Masz kilka sposobów na zrealizowanie swojego żądania:

tail -n$(expr $(cat /etc/passwd|wc -l) - 2) /etc/passwd

Gdzie / etc / passwd to twój plik

Drugie rozwiązanie może być przydatne, jeśli masz duży plik:

my1stline=$(head -n1 /etc/passwd)
my2ndline=$(head -n2 /etc/passwd|grep -v "$my1stline")
cat /etc/passwd |grep -Ev "$my1stline|$my2ndline"
Mathieu Coavoux
źródło
0

Rozwiązanie dla BSD (macOS):

Usuń pierwsze 2 linie:

tail -n $( echo "$(cat foo.txt | wc -l)-2" | bc )

Usuń ostatnie 2 linie:

head -n $( echo "$(cat foo.txt | wc -l)-2" | bc )

... niezbyt elegancki, ale wykonuje pracę!

widmo
źródło