Làm thế nào để xóa một tập tin cùng lúc với nó được thêm vào trong một kho lưu trữ?
Với bối cảnh, tôi sẽ giải thích câu hỏi này là:
Cách xóa dữ liệu khỏi đĩa ngay sau khi đọc, trước khi đọc toàn bộ tệp, để có đủ không gian cho tệp được chuyển đổi.
Việc chuyển đổi có thể là bất cứ điều gì bạn muốn làm với dữ liệu: nén, mã hóa, v.v.
Câu trả lời là đây:
<$file gzip | dd bs=$buffer iflag=fullblock of=$file conv=notrunc
Tóm lại: đọc dữ liệu, ném nó vào gzip (hoặc bất cứ điều gì bạn muốn làm với nó), đệm đầu ra để chúng tôi chắc chắn đọc nhiều hơn chúng ta viết và ghi lại vào tệp. Đây là phiên bản đẹp hơn và hiển thị đầu ra trong khi chạy:
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
Tôi sẽ đi qua nó, từng dòng một:
cat "$file"
đọc tập tin bạn muốn nén Đó là một cách sử dụng mèo vô dụng (UUOC) vì phần tiếp theo, pv, cũng có thể đọc tệp, nhưng tôi thấy điều này là đẹp hơn.
Nó đưa nó vào pv
trong đó hiển thị thông tin tiến trình ( -cN
nói với nó 'sử dụng một số loại [c] con trỏ' và cung cấp cho nó [N] ame).
Đó là đường ống gzip
mà rõ ràng là nén (đọc từ stdin, xuất ra stdout).
Mà ống vào khác pv
(xem ống).
Mà ống vào dd bs=$buffer iflag=fullblock
. Các $buffer
biến là một số, một cái gì đó giống như 50 MB. Tuy nhiên, đó là nhiều RAM bạn muốn dành cho việc xử lý an toàn tệp của bạn (như một điểm dữ liệu, bộ đệm 50 MB cho tệp 2 GB là ổn). Các iflag=fullblock
kể dd
để đọc lên đến $buffer
byte trước khi đường ống thông qua. Ban đầu, gzip sẽ viết một tiêu đề, vì vậy đầu ra của gzip sẽ nằm trong dd
dòng này . Sau đó dd
sẽ đợi cho đến khi nó có đủ dữ liệu trước khi chuyển qua, và vì vậy đầu vào có thể đọc thêm. Hơn nữa, nếu bạn có các phần không thể nén, tệp đầu ra có thể lớn hơn tệp đầu vào. Bộ đệm này đảm bảo rằng, lên đến $buffer
byte, đây không phải là vấn đề.
Sau đó, chúng tôi đi vào một dòng xem ống khác, và cuối cùng vào dd
dòng đầu ra của chúng tôi . Dòng này có of
(tệp đầu ra) và conv=notrunc
được chỉ định, trong đó notrunc
yêu cầu dd
không cắt bớt (xóa) tệp đầu ra trước khi viết. Vì vậy, nếu bạn có 500 byte A
và bạn viết 3 byte B
, tệp sẽ là BBBAAAAA...
(thay vì được thay thế bởi BBB
).
Tôi đã không bao gồm các 2>/dev/null
phần, và chúng không cần thiết. Họ chỉ thu dọn đầu ra một chút bằng cách chặn thông dd
báo "Tôi đã hoàn thành và viết nhiều byte" này. Các dấu gạch chéo ngược ở cuối mỗi dòng ( \
) làm cho bash coi toàn bộ là một lệnh lớn nối với nhau.
Đây là một kịch bản đầy đủ để sử dụng dễ dàng hơn. Thông thường, tôi đặt nó vào một thư mục có tên 'gz-in-place'. Sau đó tôi nhận ra từ viết tắt mà tôi đã tạo ra: GZIP: gnu zip tại chỗ. Vì vậy, tôi xin trình bày, 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!";
Tôi cảm thấy muốn thêm một dòng đệm khác trước gzip, để ngăn nó viết quá xa khi dd
dòng đệm lướt qua, nhưng chỉ với bộ đệm 50MiB và 1900MB /dev/urandom
dữ liệu, dường như nó vẫn hoạt động (md5sums phù hợp sau khi giải nén). Tỷ lệ đủ tốt cho tôi.
Một cải tiến khác sẽ là phát hiện viết quá xa, nhưng tôi không thấy cách thực hiện mà không loại bỏ vẻ đẹp của thứ đó và tạo ra nhiều sự phức tạp. Tại thời điểm đó, bạn cũng có thể biến nó thành một chương trình python hoàn chỉnh, thực hiện đúng tất cả (với failafes để ngăn chặn phá hủy dữ liệu).