Hapus baris terakhir dari file di Bash

332

Saya punya file foo.txt,, berisi baris-baris berikut:

a
b
c

Saya ingin perintah sederhana yang menghasilkan konten foo.txt:

a
b
Fragsworth
sumber

Jawaban:

408

Menggunakan GNU sed:

sed -i '$ d' foo.txt

The -ipilihan tidak ada di GNU sedversi lebih tua dari 3,95, sehingga Anda harus menggunakannya sebagai filter dengan file sementara:

cp foo.txt foo.txt.tmp
sed '$ d' foo.txt.tmp > foo.txt
rm -f foo.txt.tmp

Tentu saja, dalam hal ini Anda juga bisa menggunakan head -n -1bukan sed.

MacOS:

Pada Mac OS X (pada 10.7.4), setara dengan sed -iperintah di atas adalah

sed -i '' -e '$ d' foo.txt
thkala
sumber
56
Pada Mac OS X (pada 10.7.4), setara dengan sed -iperintah di atas adalah sed -i '' -e '$ d' foo.txt. Selain itu, head -n -1saat ini tidak akan berfungsi pada Mac.
mklement0
26
Bisakah Anda menjelaskan apa yang dilakukan regex '$ d'? Ini pertanyaan tentang menghapus baris terakhir, jadi saya pikir ini adalah bagian paling penting bagi semua orang yang melihat pertanyaan ini. Terima kasih :)
kravemir
38
@Miro: Tidak berarti $ dregex. Ini adalah perintah sed. dadalah perintah untuk menghapus sebuah baris, sementara itu $berarti "baris terakhir dalam file". Saat menentukan lokasi (disebut "rentang" dalam istilah) sebelum perintah, perintah itu hanya diterapkan ke lokasi yang ditentukan. Jadi, perintah ini secara eksplisit mengatakan "dalam kisaran baris terakhir dalam file, hapus saja". Cukup licin dan langsung ke intinya, jika Anda bertanya kepada saya.
Daniel Kamil Kozar
1
Bagaimana Anda menghapus baris terakhir tetapi hanya jika itu adalah baris kosong?
skan
1
@Alex: diperbarui untuk GNU sed; tidak tahu tentang berbagai versi sed BSD / UNIX / MacOS ...
thkala
279

Sejauh ini, ini adalah solusi tercepat dan paling sederhana, terutama pada file besar:

head -n -1 foo.txt > temp.txt ; mv temp.txt foo.txt

jika Anda ingin menghapus baris teratas gunakan ini:

tail -n +2 foo.txt

yang berarti jalur keluaran mulai dari jalur 2.

Jangan gunakan seduntuk menghapus garis dari atas atau bawah file - ini sangat lambat jika file tersebut besar.

John
sumber
16
head -n -1 foo.txtsudah cukup
ДМИТРИЙ МАЛИКОВ
20
head -n -1 tidak bekerja di kepala bdsutils. setidaknya tidak dalam versi macos yang digunakan, jadi ini tidak berfungsi.
johannes_lalala
23
@johannes_lalala Di Mac, Anda dapat brew install coreutils(utilitas inti GNU) dan menggunakannya gheadsebagai gantinya head.
interestinglythere
Berikut ini adalah skrip bash sederhana yang mengotomatiskannya jika Anda memiliki banyak file dengan jumlah baris berbeda di bagian bawah untuk dihapus: cat TAILfixer FILE=$1; TAIL=$2; head -n -${TAIL} ${FILE} > temp ; mv temp ${FILE}Jalankan untuk menghapus 4 baris misalnya dari myfile sebagai: ./TAILfixer myfile 4tentu saja pertama-tama membuatnya dapat dijalankan olehchmod +x TAILfixer
FatihSarigol
Jempol karena berhasil, tetapi untuk semua perbandingan sed, perintah ini juga sangat lambat
Jeff Diederiks
121

Saya mengalami masalah dengan semua jawaban di sini karena saya bekerja dengan file BESAR (~ 300GB) dan tidak ada solusi yang diskalakan. Inilah solusi saya:

dd if=/dev/null of=<filename> bs=1 seek=$(echo $(stat --format=%s <filename> ) - $( tail -n1 <filename> | wc -c) | bc )

Dengan kata lain: Cari tahu panjang file yang ingin Anda buat (panjang file dikurangi panjang panjang baris terakhir, menggunakan bc) dan, setel posisi itu menjadi akhir file (dengan memasukkan ddsatu byte /dev/nullke Itu).

Ini cepat karena tailmulai membaca dari akhir, dan ddakan menimpa file di tempat daripada menyalin (dan mengurai) setiap baris file, yang merupakan solusi apa yang dilakukan oleh solusi lain.

CATATAN: Ini menghapus baris dari file di tempat! Buat cadangan atau uji pada file dummy sebelum mencobanya di file Anda sendiri!

Yossi Farjoun
sumber
3
Aku bahkan tidak tahu tentang dd. Ini harus menjadi jawaban teratas. Terima kasih banyak! Sayang sekali solusinya tidak bekerja untuk (memblokir) file gzip.
tommy.carstensen
4
Pendekatan yang sangat, sangat pintar! Hanya sebuah catatan: ini membutuhkan dan beroperasi pada file nyata, sehingga tidak dapat digunakan pada pipa, penggantian perintah, pengalihan dan rantai semacam itu.
MestreLion
3
Ini harus menjadi jawaban teratas. Bekerja instan untuk file ~ 8 GB saya.
Peter Cogan
8
pengguna mac: dd if = / dev / null of = <filename> bs = 1 seek = $ (echo $ (stat -f =% z <filename> | cut -c 2-) - $ (tail -n1 <filename> | wc -c) | bc)
Igor
2
Pendekatan ini adalah cara untuk mencari file besar!
thomas.han
61

Untuk menghapus baris terakhir dari file tanpa membaca seluruh file atau menulis ulang apa pun , Anda dapat menggunakan

tail -n 1 "$file" | wc -c | xargs -I {} truncate "$file" -s -{}

Untuk menghapus baris terakhir dan juga mencetaknya di stdout ("pop" itu), Anda dapat menggabungkan perintah itu dengan tee:

tail -n 1 "$file" | tee >(wc -c | xargs -I {} truncate "$file" -s -{})

Perintah-perintah ini secara efisien dapat memproses file yang sangat besar. Ini mirip dengan, dan diilhami oleh, jawaban Yossi, tetapi ia menghindari penggunaan beberapa fungsi tambahan.

Jika Anda akan menggunakan ini berulang kali dan menginginkan penanganan kesalahan dan beberapa fitur lainnya, Anda dapat menggunakan poptailperintah di sini: https://github.com/donm/evenmoreutils

ohspite
sumber
3
Catatan cepat: truncatetidak tersedia pada OS X secara default. Tapi mudah untuk menginstal menggunakan Brew: brew install truncate.
Guillaume Boudreau
3
Sangat bagus. Jauh lebih baik tanpa dd. Saya takut menggunakan dd sebagai angka tunggal atau huruf yang salah bisa mengeja bencana .. +1
Yossi Farjoun
1
(LELUCON PERINGATAN) Sebenarnya, ("dd" -> "disaster") membutuhkan 1 subtitusi dan 6 insersi; nyaris "satu digit atau huruf yang salah tempat". :)
Kyle
29

Pengguna Mac

jika Anda hanya ingin output baris terakhir dihapus tanpa mengubah file itu sendiri lakukan

sed -e '$ d' foo.txt

jika Anda ingin menghapus baris terakhir dari file input itu sendiri lakukan

sed -i '' -e '$ d' foo.txt

sombong manpreet
sumber
Ini bekerja untuk saya tetapi saya tidak mengerti. Bagaimanapun, itu berhasil.
Melvin
Bendera -i ''memberitahu sed untuk memodifikasi file di tempat, sambil menyimpan cadangan dalam file dengan ekstensi yang disediakan sebagai parameter. Karena parameternya adalah string kosong, tidak ada file cadangan yang dibuat. -ememberitahu sed untuk mengeksekusi perintah. Perintah itu $ dberarti: cari baris terakhir ( $) dan hapus ( d).
Krzysztof Szafranek
24

Untuk Pengguna Mac:

Di Mac, head -n -1 tidak akan berfungsi. Dan, saya berusaha mencari solusi sederhana [tanpa khawatir tentang waktu pemrosesan] untuk menyelesaikan masalah ini hanya dengan menggunakan perintah "head" dan / atau "tail".

Saya mencoba urutan perintah berikut dan senang bahwa saya bisa menyelesaikannya hanya menggunakan perintah "tail" [dengan opsi yang tersedia di Mac]. Jadi, jika Anda menggunakan Mac, dan hanya ingin menggunakan "tail" untuk menyelesaikan masalah ini, Anda dapat menggunakan perintah ini:

cat file.txt | tail -r | tail -n +2 | ekor -r

Penjelasan:

1> tail -r: cukup membalik urutan garis dalam inputnya

2> tail -n +2: ini mencetak semua baris mulai dari baris kedua di inputnya

Sarfraaz Ahmed
sumber
2
Menginstal coreutils menggunakan Homebrew akan memberi Anda GNU head sebagai 'ghead', memungkinkan Anda melakukannyaghead -n -1
agak
13
echo -e '$d\nw\nq'| ed foo.txt
lhf
sumber
2
Untuk solusi filter yang tidak mengedit file di tempat, gunakan sed '$d'.
lhf
8
awk 'NR>1{print buf}{buf = $0}'

Pada dasarnya, kode ini mengatakan sebagai berikut:

Untuk setiap baris setelah yang pertama, cetak garis buffered

untuk setiap baris, setel ulang buffer

Buffer ditunda oleh satu baris, maka Anda akhirnya mencetak baris 1 ke n-1

Foo Bah
sumber
2
Solusi ini adalah filter dan tidak mengedit file di tempatnya.
lhf
0
awk "NR != `wc -l < text.file`" text.file |> text.file

Cuplikan ini berhasil.

Michael Kingski
sumber
Ini hanya menghapus seluruh file untuk saya.
Kartik Chugh
0

Kedua solusi ini ada di sini dalam bentuk lain. Saya menemukan ini sedikit lebih praktis, jelas, dan berguna:

Menggunakan dd:

BADLINESCOUNT=1
ORIGINALFILE=/tmp/whatever
dd if=${ORIGINALFILE} of=${ORIGINALFILE}.tmp status=none bs=1 count=$(printf "$(stat --format=%s ${ORIGINALFILE}) - $(tail -n${BADLINESCOUNT} ${ORIGINALFILE} | wc -c)\n" | bc )
/bin/mv -f ${ORIGINALFILE}.tmp ${ORIGINALFILE}

Menggunakan terpotong:

BADLINESCOUNT=1
ORIGINALFILE=/tmp/whatever
truncate -s $(printf "$(stat --format=%s ${ORIGINALFILE}) - $(tail -n${BADLINESCOUNT} ${ORIGINALFILE} | wc -c)\n" | bc ) ${ORIGINALFILE}
cahaya virtual
sumber
Aduh. Terlalu rumit.
Robert
0

Linux

$ adalah baris terakhir, d untuk menghapus:

sed '$d' ~/path/to/your/file/name

MacOS

Setara dengan sed -i

sed -i '' -e '$ d' ~/path/to/your/file/name
jasonleonhard
sumber
0

Inilah cara Anda dapat melakukannya secara manual (saya pribadi sering menggunakan metode ini ketika saya perlu menghapus baris terakhir dalam sebuah file):

vim + [FILE]

Itu +tanda ada berarti bahwa ketika file dibuka di editor teks vim, kursor akan diposisikan pada baris terakhir dari file.

Sekarang cukup tekan ddua kali pada keyboard Anda. Ini akan melakukan apa yang Anda inginkan — hapus baris terakhir. Setelah itu, tekan :diikuti oleh xdan kemudian tekan Enter. Ini akan menyimpan perubahan dan membawa Anda kembali ke shell. Sekarang, baris terakhir telah berhasil dihapus.

Michael Rybkin
sumber
2
Penekanan pada kesederhanaan hilang di sini
Peter M
-4

Ruby (1.9+)

ruby -ne 'BEGIN{prv=""};print prv ; prv=$_;' file
kurumi
sumber