Bagaimana cara menghapus file pada saat yang sama ketika itu ditambahkan dalam arsip?
Mengingat konteksnya, saya akan menafsirkan pertanyaan ini sebagai:
Cara menghapus data dari disk segera setelah dibaca, sebelum file lengkap telah dibaca, sehingga ada cukup ruang untuk file yang diubah.
Transformasi dapat berupa apa pun yang ingin Anda lakukan dengan data: mengompresi, mengenkripsi, dll.
Jawabannya adalah ini:
<$file gzip | dd bs=$buffer iflag=fullblock of=$file conv=notrunc
Singkatnya: baca data, lemparkan ke gzip (atau apa pun yang ingin Anda lakukan dengannya), buffer outputnya sehingga kami yakin akan membaca lebih banyak daripada yang kami tulis, dan menulisnya kembali ke file. Ini adalah versi yang lebih cantik dan menampilkan keluaran saat berjalan:
cat "$file" \
| pv -cN 'bytes read from file' \
| gzip \
| pv -cN 'bytes received from compressor' \
| dd bs=$buffer iflag=fullblock 2>/dev/null \
| pv -cN 'bytes written back to file' \
| dd of="$file" conv=notrunc 2>/dev/null
Saya akan melewatinya, baris demi baris:
cat "$file"
membaca file yang ingin Anda kompres. Ini adalah penggunaan cat (UUOC) yang tidak berguna karena bagian selanjutnya, pv, juga dapat membaca file, tetapi saya menemukan ini lebih cantik.
Ini menyalurkannya ke pv
yang menunjukkan informasi kemajuan ( -cN
memberi tahu 'gunakan semacam [c] ursor' dan beri nama [N]).
Itu pipa gzip
yang jelas melakukan kompresi (membaca dari stdin, keluaran ke stdout).
Itu pipa ke yang lain pv
(tampilan pipa).
Pipa itu menjadi dd bs=$buffer iflag=fullblock
. The $buffer
variabel adalah angka, sesuatu seperti 50 megabyte. Namun berapa banyak RAM yang ingin Anda dedikasikan untuk penanganan file Anda secara aman (sebagai titik data, buffer 50MB untuk file 2GB baik-baik saja). The iflag=fullblock
memberitahu dd
untuk membaca hingga $buffer
byte sebelum pipa melalui. Pada awalnya, gzip akan menulis header, jadi output gzip akan mendarat di dd
baris ini . Kemudian dd
akan menunggu hingga memiliki cukup data sebelum disalurkan, sehingga input dapat dibaca lebih lanjut. Selain itu, jika Anda memiliki bagian yang tidak dapat dikompresi, file output mungkin lebih besar dari file input. Buffer ini memastikan bahwa, hingga $buffer
byte, ini bukan masalah.
Kemudian kita pergi ke jalur tampilan pipa lain, dan akhirnya ke dd
jalur output kami . Baris ini memiliki of
(file output) dan conv=notrunc
ditentukan, di mana notrunc
memberitahu dd
untuk tidak memotong (menghapus) file output sebelum menulis. Jadi, jika Anda memiliki 500 byte A
dan Anda menulis 3 byte B
, file tersebut akan BBBAAAAA...
(bukannya diganti oleh BBB
).
Saya tidak menutupi 2>/dev/null
bagian - bagiannya, dan itu tidak perlu. Mereka hanya merapikan output sedikit dengan menekan dd
pesan "Saya sudah selesai dan menulis ini banyak byte". Garis miring terbalik pada akhir setiap baris ( \
) membuat bash memperlakukan semuanya sebagai satu perintah besar yang saling menyambungkan satu sama lain.
Berikut ini skrip lengkap untuk memudahkan penggunaan. Secara anekdot, saya meletakkannya di folder bernama 'gz-in-place'. Saya kemudian menyadari akronim yang saya buat: GZIP: gnu zip in-place. Jadi dengan ini saya hadir, GZIP.sh:
#!/usr/bin/env bash
### Settings
# Buffer is how many bytes to buffer before writing back to the original file.
# It is meant to prevent the gzip header from overwriting data, and in case
# there are parts that are uncompressible where the compressor might exceed
# the original filesize. In these cases, the buffer will help prevent damage.
buffer=$((1024*1024*50)) # 50 MiB
# You will need something that can work in stream mode from stdin to stdout.
compressor="gzip"
# For gzip, you might want to pass -9 for better compression. The default is
# (typically?) 6.
compressorargs=""
### End of settings
# FYI I'm aware of the UUOC but it's prettier this way
if [ $# -ne 1 ] || [ "x$1" == "x-h" ] || [ "x$1" == "x--help" ]; then
cat << EOF
Usage: $0 filename
Where 'filename' is the file to compress in-place.
NO GUARANTEES ARE GIVEN THAT THIS WILL WORK!
Only operate on data that you have backups of.
(But you always back up important data anyway, right?)
See the source for more settings, such as buffer size (more is safer) and
compression level.
The only non-standard dependency is pv, though you could take it out
with no adverse effects, other than having no info about progress.
EOF
exit 1;
fi;
b=$(($buffer/1024/1024));
echo "Progressing '$1' with ${b}MiB buffer...";
echo "Note: I have no means of detecting this, but if you see the 'bytes read from";
echo "file' exceed 'bytes written back to file', your file is now garbage.";
echo "";
cat "$1" \
| pv -cN 'bytes read from file' \
| $compressor $compressorargs \
| pv -cN 'bytes received from compressor' \
| dd bs=$buffer iflag=fullblock 2>/dev/null \
| pv -cN 'bytes written back to file' \
| dd of="$1" conv=notrunc 2>/dev/null
echo "Done!";
Saya merasa ingin menambahkan garis buffering lain sebelum gzip, untuk mencegahnya menulis terlalu jauh ketika dd
garis buffering memancar, tetapi dengan hanya buffer 50MiB dan /dev/urandom
data 1900MB , sepertinya sudah berfungsi juga (md5sum cocok setelah dekompresi). Rasio yang cukup bagus untuk saya.
Peningkatan lainnya adalah deteksi tulisan yang terlalu jauh, tetapi saya tidak melihat cara melakukannya tanpa menghilangkan keindahan benda itu dan menciptakan banyak kerumitan. Pada titik itu, Anda mungkin juga membuatnya menjadi program python sepenuhnya yang melakukan semuanya dengan benar (dengan failafes untuk mencegah kerusakan data).