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 pvtrong đó hiển thị thông tin tiến trình ( -cNnó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 gzipmà 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 $bufferbiế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=fullblockkể ddđể đọc lên đến $bufferbyte 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 dddòng này . Sau đó ddsẽ đợ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 $bufferbyte, đâ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 dddò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 đó notruncyêu cầu ddkhô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 Avà 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/nullphầ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 ddbá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 dddòng đệm lướt qua, nhưng chỉ với bộ đệm 50MiB và 1900MB /dev/urandomdữ 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).