Bagaimana cara memotong file demi baris?

13

Saya memiliki banyak file, beberapa di antaranya sangat panjang. Saya ingin memotongnya ke ukuran tertentu jika mereka lebih besar dengan menghapus ujung file. Tapi saya hanya ingin menghapus seluruh baris. Bagaimana saya bisa melakukan ini? Rasanya seperti hal yang akan ditangani oleh toolchain Linux tapi saya tidak tahu perintah yang tepat.

Misalnya, saya memiliki file 120.000 byte dengan 300-byte baris dan saya mencoba memotongnya menjadi 10.000 byte. 33 baris pertama harus tetap (9900 byte) dan sisanya harus dipotong. Saya tidak ingin memotong pada 10.000 byte persis, karena itu akan meninggalkan garis parsial.

Tentu saja file memiliki panjang yang berbeda dan garis-garisnya tidak semuanya sama panjang.

Idealnya file yang dihasilkan akan dibuat sedikit lebih pendek daripada sedikit lebih lama (jika breakpoint ada di garis panjang) tapi itu tidak terlalu penting, bisa jadi sedikit lebih lama jika itu lebih mudah. Saya ingin perubahan dilakukan langsung ke file (well, mungkin file baru disalin di tempat lain, yang asli dihapus, dan file baru dipindahkan, tapi itu sama dari POV pengguna). Sebuah solusi yang mengalihkan data ke banyak tempat dan kemudian kembali mengundang kemungkinan merusak file dan saya ingin menghindari itu ...

Charles
sumber
Menghapus jawaban saya ... Saya kira ukuran file dalam Bytes tidak terlalu jelas, maaf. Mungkin Anda bisa mengedit pertanyaan Anda dan mengklarifikasi bagian itu (misalnya dengan contoh)?
slhck
@ Slhck: Maaf melihat Anda kehilangan perwakilan hanya karena saya tidak jelas ... biarkan saya melihat apakah saya dapat memperbaikinya.
Charles
Jangan khawatir, aku seharusnya bertanya, maaf :)
slhck

Jawaban:

1

The sed/ wckompleksitas dapat dihindari dalam jawaban sebelumnya jika awkdigunakan. Menggunakan contoh yang disediakan dari OP (menampilkan baris lengkap sebelum 10.000 byte):

awk '{i += (length() + 1); if (i <= 10000) print $ALL}' myfile.txt

Juga menunjukkan baris lengkap yang berisi 10.000 byte jika byte itu tidak di akhir baris:

awk '{i += (length() + 1); print $ALL; if (i >= 10000) exit}' myfile.txt

Jawaban di atas mengasumsikan:

  1. File teks berasal dari terminator garis Unix ( \n). Untuk file teks Dos / Windows ( \r\n), ubah length() + 1kelength() + 2
  2. File teks hanya berisi karakter byte tunggal. Jika ada karakter multibyte (seperti di bawah lingkungan unicode), atur lingkungan LC_CTYPE=Cuntuk memaksakan interpretasi pada level byte.
Abel Cheung
sumber
14

The sedpendekatan baik-baik saja, tapi loop atas semua lini tidak. Jika Anda tahu berapa banyak baris yang ingin Anda pertahankan (untuk memiliki contoh, saya menggunakan 99 di sini), Anda dapat melakukannya seperti ini:

sed -i '100,$ d' myfile.txt

Penjelasan: sedadalah prosesor ekspresi reguler. Dengan opsi yang -idiberikan, ia memproses file secara langsung ("inline") - alih-alih hanya membacanya dan menulis hasilnya ke output standar. 100,$hanya berarti "dari baris 100 hingga akhir file" - dan diikuti oleh perintah d, yang mungkin Anda tebak dengan benar artinya "hapus". Jadi singkatnya, perintah itu berarti: "Hapus semua baris dari baris 100 ke akhir file dari myfile.txt". 100 adalah baris pertama yang akan dihapus, karena Anda ingin mempertahankan 99 baris.

Sunting: Jika, di sisi lain, ada file log tempat Anda ingin menyimpan mis. 100 baris terakhir :

[ $(wc -l myfile.txt) -gt 100 ] && sed -i "1,$(($(wc -l myfile.txt|awk '{print $1}') - 100)) d" myfile.txt

Apa yang terjadi disini:

  • [ $(wc -l myfile.txt) -gt 100 ]: lakukan langkah-langkah berikut hanya jika file memiliki lebih dari 100 baris
  • $((100 - $(wc -l myfile.txt|awk '{print $1}'))): menghitung jumlah baris yang akan dihapus (yaitu semua baris file kecuali (terakhir) yang dipertahankan 100)
  • 1, $((..)) d: hapus semua baris dari baris pertama hingga baris yang dihitung

EDIT: karena pertanyaannya baru saja diedit untuk memberikan rincian lebih lanjut, saya akan memasukkan informasi tambahan ini dengan jawaban saya juga. Fakta yang ditambahkan adalah:

  • ukuran tertentu harus tetap dengan file (10.000 byte)
  • setiap baris memiliki ukuran spesifik dalam byte (300 byte dalam contoh)

Dari data ini dimungkinkan untuk menghitung jumlah baris untuk tetap sebagai "/", yang dengan contoh berarti 33 baris. Istilah shell untuk perhitungan: $((size_to_remain / linesize))(setidaknya di Linux menggunakan Bash, hasilnya adalah integer). Perintah yang disesuaikan sekarang akan berbunyi:

# keep the start of the file (OPs question)
sed -i '34,$ d' myfile.txt
# keep the end of the file (my second example)
[ $(wc -l myfile.txt) -gt 33 ] && sed -i "1,33 d" myfile.txt

Karena ukuran diketahui sebelumnya, tidak ada lagi kebutuhan untuk perhitungan yang melekat pada sedperintah. Tetapi untuk fleksibilitas, di dalam beberapa skrip shell kita dapat menggunakan variabel.

Untuk pemrosesan bersyarat berdasarkan ukuran file, seseorang dapat menggunakan "test" berikut ini:

[ "$(ls -lk $file | awk ' {print $5}')" -gt 100 ] &&

yang berarti: "jika ukurannya $filemelebihi 100kB, lakukan ..." ( ls -lkdaftar ukuran file dalam kB pada posisi 5, maka awkdigunakan untuk mengekstrak persis ini).

Izzy
sumber
OP ingin memotong file berdasarkan ukuran byte tertentu - tidak hanya panjang dalam hal garis. Saya menghapus jawaban saya yang melibatkan head -n.
slhck
@ slhck Terima kasih atas pemberitahuannya. Ya, OP baru saja mengedit pertanyaannya untuk memperjelas niatnya. Karena ia memiliki cara untuk menghitung berapa banyak byte yang dimiliki setiap baris, pada prinsipnya jawaban saya tetap valid - karena ia dapat menghitung jumlah baris yang tersisa, dan kemudian menggunakan pendekatan saya untuk menangani file. Mungkin saya membuat komentar singkat tentang itu dalam jawaban saya.
Izzy
Tidak - ukurannya tidak diketahui sebelumnya. Itu contohnya. Setiap file akan memiliki ukuran dan garis yang berbeda dengan panjang tidak teratur. Beberapa file tidak perlu terpotong sama sekali.
Charles
Oh, lagi ... Ya, beberapa hal sulit dijelaskan dengan jelas (terlalu banyak segi). Adapun file yang tidak perlu dipotong, itu mungkin didasarkan pada ukuran file? Itu bisa ditutupi. Tetapi jika tidak ada ukuran garis rata-rata yang diketahui, bagian ini menjadi sulit - saya tidak dapat memikirkan solusi yang mudah (tanpa terlalu banyak overhead) saat ini.
Izzy
Yang bisa saya pikirkan saat ini akan melibatkan misalnya mendapatkan n baris pertama, menghitung panjang rata-rata berdasarkan mereka, dan menggunakan nilai ini. Apakah itu membantu Anda?
Izzy
0

Gagal menemukan perintah untuk melakukan ini, saya menulis skrip cepat (tidak diuji):

#!/bin/sh

# Usage: $0 glob.* 25000
# where glob.* is a wildcard pattern and 25000 is the maximum number of bytes.

limit=20000
tmp=/tmp/trim
[[ "$2" == +([0-9]) ]] || limit=$2
limit=`expr $len + 1`
for file in $1;
do
    [[ `wc -c $file` -lt $limit ]] && continue
    head -c $file > $tmp
    sed '$d' $tmp
    $tmp > $file
done
Charles
sumber
-1

Anda dapat menggunakan perintah linux sed untuk menghapus baris dari file. Perintah berikut menghapus baris terakhir dari filename.txt:

sed '$d' filename.txt

Dengan awk atau temukan, Anda dapat mencari pola yang cocok dengan perintah sed Anda. Pertama Anda mencari dengan awk atau mencari file yang ingin Anda persingkat dan kemudian Anda dapat menghapus garis dengan sed.

kockiren
sumber
-1

Saya melakukan sesuatu yang mirip dengan ekor. Untuk hanya menyimpan 10.000 baris terakhir dalam hal ini:

TMP=$(tail -n 10000 /path/to/some/file 2>/dev/null) && echo "${TMP}" > /path/to/some/file
Bill M
sumber