Hapus n byte file pertama

32

Saya punya masalah ekstrem, dan semua solusi yang bisa saya bayangkan rumit. Menurut pengalaman UNIX / Linux saya pasti ada cara mudah.

Saya ingin menghapus 31 byte pertama dari setiap file di /foo/. Setiap file cukup panjang. Yah, saya yakin seseorang akan memberikan saya solusi yang sangat mudah saya tidak bisa bayangkan. Mungkin awk?

von der tann
sumber
2
Setiap solusi awk / sed / ed akan berorientasi garis, jadi jika Anda tidak tahu baris pertama setidaknya 31 karakter maka komplikasi terjadi.
glenn jackman

Jawaban:

28
for file in /foo/*
do
  if [ -f "$file" ]
  then
    dd if="$file" of="$file.truncated" bs=31 skip=1 && mv "$file.truncated" "$file"
  fi
done

atau lebih cepat, terima kasih atas saran Gilles:

for file in /foo/*
    do
      if [ -f $file ]
      then
        tail +32c $file > $file.truncated && mv $file.truncated $file
      fi
    done

Catatan: Ekor ekor menentukan "-c +32" bukan "+ 32c" tetapi ekor default Solaris tidak menyukainya:

   $ /usr/bin/tail -c +32 /tmp/foo > /tmp/foo1
    tail: cannot open input

/usr/xpg4/bin/tail tidak masalah dengan kedua sintaksis.

Jlliagre
sumber
1
Menyarankan di ddsini berlebihan, taillebih tepat (lebih sederhana, lebih sedikit risiko kesalahan ketik pembunuh, tidak ada pesan palsu di stderr).
Gilles 'SANGAT berhenti menjadi jahat'
Kamu benar. Saya biasanya menghindari perintah yang dimaksudkan untuk memproses file teks saat memproses yang mungkin biner tetapi "tail + 32c" akan bekerja di sini.
jlliagre
1
@ jlliagre: Anda telah menulis cut (bukankah seharusnya itu ekor? ... asis, itu tidak berhasil untuk saya ...
Peter.O
Tentu saja, itu buntut. Maaf tentang ketidakcocokan.
jlliagre
@jlliagre: Pada Solaris, Anda harus memiliki /usr/xpg4/bindepan /usr/binpada Anda PATH, atau Anda akan terjebak di awal 1990-an. Banyak penyatuan (mis. GNU, BusyBox) tidak lagi mendukung +32csintaksis historis , dan menganggapnya sebagai file yang dipanggil +32c(seperti yang diperlukan POSIX).
Gilles 'SANGAT berhenti menjadi jahat'
12

Perintah berikut memotong 31 byte pertama dari $file (menggunakan $file~sebagai temp. Copy):

dd if="$file" of="$file~" bs=1 skip=31
mv "$file~" "$file"

Anda hanya perlu mendaftar atau findsemua file di bawah /foo/dan menjalankan keduanya di atas untuk setiap yang $fileditemukan.

alex
sumber
1
Menukar bs dan melewatkan nilai akan meningkatkan kinerja.
jlliagre
10

tail -c +32output inputnya minus 31 byte pertama. (Ya, argumennya mati satu per satu.) Untuk mengedit file di tempatnya, gunakan spons dalam satu lingkaran, atau jika Anda tidak memilikinya dan tidak ingin repot, lakukan tugasnya di shell:

for x in /foo/*; do tail -c +32 "$x" | sponge "$x"; done
for x in /foo/*; do tail -c +32 "$x" >"$x.new" && mv "$x.new" "$x"; done

Jika perintah terputus karena alasan apa pun (mis. Kegagalan daya), mungkin sulit untuk mengetahui di mana Anda tinggalkan. Menulis file baru ke direktori terpisah akan membuat segalanya lebih mudah.

mkdir /foo.tmp
cd /foo
for x in *; do tail -c +42 -- "$x" >"/foo.tmp/$x" && rm -- "$x"; done
mv /foo.tmp/* /foo
rmdir /foo.tmp

Jika file benar-benar besar (seperti pada, cukup besar yang memiliki dua salinan bahkan satu adalah masalah), Anda dapat menggunakan salah satu teknik yang disebutkan di utas ini .

Gilles 'SANGAT berhenti menjadi jahat'
sumber
2

Anda dapat menggunakan Vim dalam mode Ex:

for each in /foo/*
do
  ex -sc '%!tail -c+32' -cx "$each"
done
  1. % pilih semua garis

  2. ! jalankan perintah

  3. x Simpan dan tutup

Steven Penny
sumber