Bagaimana cara mendapatkan perilaku terbalik untuk `tail` dan` head`?

55

Apakah ada cara untuk head/ taildokumen dan mendapatkan output terbalik; karena Anda tidak tahu ada berapa baris dalam dokumen?

Yaitu saya hanya ingin mendapatkan semuanya kecuali 2 baris pertama foo.txtuntuk ditambahkan ke dokumen lain.

chrisjlee
sumber

Jawaban:

57

Anda dapat menggunakan ini untuk menghapus dua baris pertama :

tail -n +3 foo.txt

dan ini untuk menghilangkan dua baris terakhir :

head -n -2 foo.txt

(dengan asumsi file berakhir dengan \nuntuk yang terakhir)


Sama seperti untuk penggunaan standar taildan headoperasi ini tidak merusak. Gunakan >out.txtjika Anda ingin mengarahkan output ke beberapa file baru:

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

Dalam hal out.txtsudah ada, itu akan menimpa file ini. Gunakan >>out.txtalih-alih >out.txtjika Anda ingin hasilnya ditambahkan out.txt.

Stéphane Gimenez
sumber
3
re "head, ketika file diakhiri dengan \n" .. Ia berfungsi untuk semua bilangan bulat negatif selain -n -0yang tidak mengembalikan apa-apa , sama seperti -n 0(menggunakan: head (GNU coreutils) 7.4) ... Namun ketika sebuah trailing \nhadir, -n -0apakah mencetak seperti yang mungkin diharapkan dari -, yaitu. itu mencetak seluruh file ... Jadi itu berfungsi untuk semua nilai negatif yang tidak nol .. tetapi -0gagal ketika tidak ada trailing\n
Peter.O
@ Fred: Memang aneh ... (sama dengan 8,12 di sini).
Stéphane Gimenez
Apakah operasi ini merusak? Seperti yang saya inginkan untuk menyalin kebalikan dari dua baris pertama dokumen ke yang lain?
chrisjlee
@ Chris: Tidak, mereka hanya mencetak hasilnya pada "output standar" mereka, yang biasanya terhubung ke terminal. Saya telah menambahkan beberapa detail tentang cara mengarahkan output ke beberapa file.
Stéphane Gimenez
7
head -n -2tidak kompatibel dengan POSIX .
l0b0
9

Jika Anda ingin semua kecuali saluran N-1 pertama, hubungi tailnomor yang ada +N. (Angka adalah angka dari baris pertama yang ingin Anda pertahankan, mulai dari 1, yaitu +1 berarti mulai dari atas, +2 berarti lewati satu baris dan seterusnya).

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

Tidak ada cara mudah dan portabel untuk melewati garis N terakhir. GNU headmemungkinkan head -n +Nsebagai bagian dari tail -n +N. Jika tidak, jika Anda memiliki tac(mis. GNU atau Busybox), Anda dapat menggabungkannya dengan tail:

tac | tail -n +3 | tac

Portable, Anda dapat menggunakan filter awk (belum diuji):

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

Jika Anda ingin menghapus beberapa baris terakhir dari file besar, Anda dapat menentukan byte offset potongan untuk memotong kemudian melakukan pemotongan dengan 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))"

Anda tidak dapat memotong file di tempat di awal, meskipun jika Anda perlu menghapus beberapa baris pertama dari file besar, Anda dapat memindahkan konten di sekitar .

Gilles 'SANGAT berhenti menjadi jahat'
sumber
Pada beberapa sistem (seperti Linux modern), Anda dapat memotong (runtuh) file di tempat di awal, tetapi biasanya hanya dengan jumlah yang kelipatan dari ukuran blok FS (jadi tidak benar-benar berguna dalam kasus ini).
Stéphane Chazelas
3

Dari tailhalaman manual (GNU tail, yaitu):

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

Dengan demikian, berikut ini harus menambahkan semua kecuali 2 baris pertama somefile.txtke anotherfile.txt:

tail --lines=+3 somefile.txt >> anotherfile.txt
Steven Monday
sumber
3

Untuk menghapus n baris pertama, GNU sed dapat digunakan. Misalnya jika n = 2

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

The !berarti "mengecualikan interval ini". Seperti yang dapat Anda bayangkan, hasil yang lebih rumit dapat diperoleh, misalnya

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

itu akan menunjukkan baris 3,4,5,7. Lebih banyak kekuatan berasal dari penggunaan ekspresi reguler, bukan alamat.

Batasannya adalah bahwa nomor garis harus diketahui sebelumnya.

enzotib
sumber
1
Kenapa tidak adil sed 1,2d? Simpler biasanya lebih baik. Juga, tidak ada dalam contoh Anda yang spesifik untuk GNU Sed; perintah Anda semua menggunakan fitur POSIX standar Sed .
Wildcard
1

Anda dapat menggunakan diffuntuk membandingkan output dari head/ tailke file asli dan kemudian menghapus apa yang sama, sehingga mendapatkan kebalikannya.

diff --unchanged-group-format='' foo.txt <(head -2 foo.txt)
soboban
sumber
1

Sedangkan tail -n +4untuk output file mulai dari baris ke-4 (semua kecuali 3 baris pertama) adalah standar dan portabel, headrekannya ( head -n -3, semua kecuali 3 baris terakhir) tidak.

Mudah dibawa, Anda akan melakukan:

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

Atau:

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

(Waspadalah bahwa pada beberapa sistem di mana sedmemiliki ruang pola ukuran terbatas, yang tidak skala ke nilai besar n).

Atau:

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

Jika <infilefile biasa, lseek()-able, maka ya, tentu saja, silakan saja. Di atas adalah konstruk yang sepenuhnya didukung POSIXly.

mikeserv
sumber
0

Pendekatan saya mirip dengan Gilles tapi saya malah membalikkan file dengan kucing dan pipa itu dengan perintah kepala.

tac -r thefile.txt | head thisfile.txt (menggantikan file)

Abe
sumber
0

Semoga saya mengerti dengan jelas kebutuhan Anda.

Anda memiliki beberapa cara untuk menyelesaikan permintaan Anda:

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

Di mana / etc / passwd adalah file Anda

Solusi ke-2 mungkin bermanfaat jika Anda memiliki file besar:

my1stline=$(head -n1 /etc/passwd)
my2ndline=$(head -n2 /etc/passwd|grep -v "$my1stline")
cat /etc/passwd |grep -Ev "$my1stline|$my2ndline"
Mathieu Coavoux
sumber
0

Solusi untuk BSD (macOS):

Hapus 2 baris pertama:

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

Hapus 2 baris terakhir:

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

... tidak terlalu elegan tetapi menyelesaikan pekerjaan!

spektrum
sumber