Cara menggunakan sed untuk menghapus n baris terakhir file

148

Saya ingin menghapus beberapa n baris dari akhir file. Bisakah ini dilakukan dengan menggunakan sed?

Misalnya, untuk menghapus garis dari 2 hingga 4, saya bisa menggunakan

$ sed '2,4d' file

Tapi saya tidak tahu nomor barisnya. Saya dapat menghapus baris terakhir menggunakan

$sed $d file

tapi saya ingin tahu cara menghapus n baris dari akhir. Tolong beri tahu saya cara melakukannya menggunakan sed atau metode lain.

mezda
sumber
2
@arashkordi: yang menggunakan jumlah baris dari bagian atas file, bukan bagian bawah.
ams
Pertanyaan terkait pada pengguna super .
Thor
//, Mungkin mempertimbangkan ulang pertanyaan itu untuk membuatnya lebih umum daripada hanya sed ?
Nathan Basanese
1
sed $d filemengembalikan kesalahan. Sebagai gantinya, $ d harus berada dalam tanda kutip, seperti ini:sed '$d' file
Akronix

Jawaban:

221

Saya tidak tahu sed, tapi itu bisa dilakukan dengan head:

head -n -2 myfile.txt
am
sumber
19
+1 untuk kesederhanaan. Perintah sed yang setara adalah butt jelek: sed -e :a -e '$d;N;2,5ba' -e 'P;D' file( ,5untuk 5 baris terakhir).
Marc B
62
Perhatikan bahwa ini berfungsi dengan beberapa versi head, tetapi tidak standar. Memang, standar untuk headnegara:The application shall ensure that the number option-argument is a positive decimal integer.
William Pursell
21
Untuk menambah jawaban @ WilliamPursell ... Pada shell default Mac OS, ini tidak berfungsi.
JDS
7
Juga di BSD, tetapi pertanyaannya ditandai Linux.
ams
3
minus 1, karena, selain osx, ini tidak berfungsi untuk saya di Ubuntu14 -head: illegal line count -- -2
Michael Durrant
30

Jika hardcoding n adalah opsi, Anda dapat menggunakan panggilan berurutan untuk melakukan sed. Misalnya, untuk menghapus tiga baris terakhir, hapus tiga baris terakhir tiga kali:

sed '$d' file | sed '$d' | sed '$d'
Kyle
sumber
2
Ini, plus -i untuk mengedit file di tempat daripada membuangnya ke stdout, bekerja untuk saya.
WAF
27

Dari sed one-liners :

# 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

Tampaknya apa yang Anda cari.

qstebom
sumber
@Tor Anda dapat mengubah berapa baris yang dihapus dari file dengan mengubah 10in'$d;N;2,10ba'
Alexej Magura
1
@AlexejMagura: Saya mengomentari versi sebelumnya dari jawabannya.
Thor
22

Solusi lucu & sederhana seddan tac:

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

CATATAN

  • "diperlukan tanda kutip ganda untuk shell untuk mengevaluasi $nvariabel dalam sedperintah. Dalam tanda kutip tunggal, tidak ada interpolasi akan dilakukan.
  • tacadalah catterbalik, lihatman 1 tac
  • yang {}di sedyang ada untuk memisahkan $n& d(jika tidak, shell mencoba untuk interpolasi tidak ada $ndvariabel)
Gilles Quenot
sumber
7
fyi no tacon osx
Michael Durrant
1
@MichaelDurrant port info coreutils|| brew info coreutils;
Suara
19

Gunakan sed, tetapi biarkan shell melakukan perhitungan, dengan tujuan untuk menggunakan dperintah dengan memberikan rentang (untuk menghapus 23 baris terakhir):

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

Untuk menghapus 3 baris terakhir, dari dalam ke luar:

$(wc -l < file)

Memberi jumlah baris file: katakan 2196

Kami ingin menghapus 23 baris terakhir, jadi untuk sisi atau rentang kiri:

$((2196-22))

Memberikan: 2174 Jadi interpretasi asli setelah shell adalah:

sed -i '2174,$d' file

Dengan -imelakukan inplace edit, file sekarang 2173 baris!

Jika Anda ingin menyimpannya ke file baru, kodenya adalah:

sed -i '2174,$d' file > outputfile
lzc
sumber
15

Anda bisa menggunakan kepala untuk ini.

Menggunakan

$ head --lines=-N file > new_file

di mana N adalah jumlah baris yang ingin Anda hapus dari file.

Isi file asli minus baris N terakhir sekarang di new_file

richard
sumber
8

Hanya untuk kelengkapan saya ingin menambahkan solusi saya. Saya akhirnya melakukan ini dengan standar ed:

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

Ini menghapus 2 baris terakhir menggunakan pengeditan di tempat (meskipun tidak menggunakan file sementara di /tmp!!)

Michel
sumber
5

Untuk memotong file yang sangat besar benar-benar di tempat kami memiliki truncateperintah. Tidak tahu tentang garis, tetapi tail+ wcdapat mengonversi garis menjadi byte:

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

Ada kondisi ras yang jelas jika file tersebut ditulis pada waktu yang bersamaan. Dalam hal ini mungkin lebih baik untuk digunakan head- ia menghitung byte dari awal file (IO disk pikiran), jadi kami akan selalu memotong pada batas baris (mungkin lebih banyak baris dari yang diharapkan jika file secara aktif ditulis):

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

Satu garis yang mudah digunakan jika Anda gagal mencoba memasukkan kata sandi sebagai ganti nama pengguna:

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

Ini mungkin bekerja untuk Anda (sed GNU):

sed ':a;$!N;1,4ba;P;$d;D' file
potong
sumber
Bagus. Anda melewatkan tanda kutip akhir ( ').
Thor
maaf untuk komentar yang sangat terlambat tetapi saya mencoba dan (adpated untuk AIX / posix saya) gagal mencetak semua kecuali baris terakhir. Membaca kode, saya tidak mengerti bagaimana empat baris terakhir dihapus, loop eksplisit pada 4 baris pertama sehingga tentu saja loop setelah d, Datau Pdengan sed GNU dan bukan pada versi posix. Tampaknya baris tidak dicetak setelah Ddan tetap di buffer kerja untuk loop baru tanpa melewati ke 'akhir' baris tindakan.
NeronLeVelu
@NeronLeVelu program membaca di jendela 4 baris ke dalam ruang pola dan kemudian menambahkan baris berikutnya dan mencetak yang pertama hingga mencapai akhir file di mana ia menghapus baris yang tersisa.
potong
@ potong ya, saya menguji pada sed GNU dan itu menjaga buffer antara garis di mana posix membongkar setelah Dmembuat efek sebaliknya.
NeronLeVelu
4

Sebagian besar jawaban di atas tampaknya membutuhkan perintah / ekstensi GNU:

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

Untuk solusi yang sedikit lebih mudah dibawa:

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

ATAU

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

ATAU

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

Di mana "10" adalah "n".

Hati gelap
sumber
2

Dengan jawaban di sini Anda sudah belajar bahwa sed bukan alat terbaik untuk aplikasi ini.

Namun saya pikir ada cara untuk melakukan ini dalam menggunakan sed; idenya adalah menambahkan garis N untuk menahan ruang sampai Anda dapat membaca tanpa menekan EOF. Ketika EOF dipukul, cetak konten ruang tunggu dan keluar.

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

Perintah sed di atas akan menghilangkan 5 baris terakhir.

Rishabh Sagar
sumber
2

Coba perintah berikut:

n = line number
tail -r file_name | sed '1,nd' | tail -r
Rishav Kaushal
sumber
Tolong jelaskan bagaimana ini bekerja. FYI - untuk menginisialisasi variabel shell, kita seharusnya tidak memiliki ruang di sekitar =.
codeforester
@codeforester Baris pertama adalah komentar yang saya kira. Dia hanya menggunakan tail -rmana yang orang lain gunakan tacuntuk membalikkan urutan garis, dan membuat nbaris terakhir menjadi nbaris pertama.
Nicolas Melay
2

Itu bisa dilakukan dalam 3 langkah:

a) Hitung jumlah baris dalam file yang ingin Anda edit:

 n=`cat myfile |wc -l`

b) Kurangi dari jumlah itu jumlah baris yang akan dihapus:

 x=$((n-3))

c) Katakan sed untuk menghapus dari nomor baris ( $x) sampai akhir:

 sed "$x,\$d" myfile
Rey Lefebvre
sumber
Matematika buruk Jika Anda harus menghapus 3 baris, kurangi 3 tetapi TAMBAH 1. Dengan matematika Anda, jika file memiliki 10 baris, 10 - 3 = 7 dan Anda gunakan seduntuk menghapus baris 7 hingga 10, itu adalah 4 baris, bukan 3.
mathguy
1

Anda bisa mendapatkan jumlah total garis dengan wc -l <file>dan menggunakan

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

SWW13
sumber
1

Ini akan menghapus 3 baris terakhir dari file:

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

pengguna5780554
sumber
1
Ini terlalu mahal untuk file besar - seluruh file harus dibaca dan ditulis ulang berkali-kali! Dan banyak garpu untuk sed.
codeforester
sementara ini mungkin bukan solusi yang paling efisien, ini adalah solusi yang paling mudah dan mudah dipahami
Dharam
0

Saya lebih suka solusi ini;

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

di mana N adalah jumlah baris yang harus dihapus.

p014k
sumber
0
sed -n ':pre
1,4 {N;b pre
    }
:cycle
$!{P;N;D;b cycle
  }' YourFile

versi posix

NeronLeVelu
sumber
0

Untuk menghapus 4 baris terakhir:

$ nl -b a file | sort -k1,1nr | sed '1, 4 d' | sort -k1,1n | sed 's/^ *[0-9]*\t//'   
mstafreshi
sumber
agak berat (terutama dalam sumber daya) dibandingkan dengan kepala. Paling tidak tac file | sed '1,4d' | tackarena mengurutkan kemudian menghapus banyak biaya prefix dari sumber daya.
NeronLeVelu
@NeronLeVelu Anda benar tetapi tidak ada tacperintah adalah beberapa sistem (misalnya FreeBSD?)
mstafreshi
0

Saya datang dengan ini, di mana n adalah jumlah baris yang ingin Anda hapus:

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

Ini sedikit bundaran, tapi saya pikir mudah diikuti.

  1. Hitung jumlah baris dalam file utama
  2. Kurangi jumlah garis yang ingin Anda hapus dari hitungan
  3. Cetak jumlah baris yang ingin Anda simpan dan simpan dalam file temp
  4. Ganti file utama dengan file temp
  5. Hapus file temp
coyotetechy
sumber
0

Untuk menghapus baris N terakhir file, Anda dapat menggunakan konsep yang sama

$ sed '2,4d' file

Anda dapat menggunakan kombo dengan perintah ekor untuk membalikkan file: jika N adalah 5

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

Dan cara ini juga berjalan di mana head -n -5 fileperintah tidak berjalan (seperti pada mac!).

Alessandra Bilardi
sumber
-2

Ini akan menghapus 12 baris terakhir

sed -n -e :a -e '1,10!{P;N;D;};N;ba'
R. Kumar
sumber
1
Sedikit penjelasan akan membantu.
Richard Wiseman