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.


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

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'

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.


@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)

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

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


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!!)


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

4

Ini mungkin bekerja untuk Anda (sed GNU):

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

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".


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.


2

Coba perintah berikut:

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

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

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>


1

Ini akan menghapus 3 baris terakhir dari file:

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


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.


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

versi posix


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//'   

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

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!).


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.