Trong Unix, hầu hết các trình soạn thảo hoạt động bằng cách tạo một tệp tạm thời mới chứa nội dung được chỉnh sửa. Khi tệp đã chỉnh sửa được lưu, tệp gốc sẽ bị xóa và tệp tạm thời được đổi tên thành tên gốc. (Tất nhiên, có nhiều biện pháp bảo vệ khác nhau để ngăn chặn dataloss.) Ví dụ, đây là kiểu được sử dụng bởi sed
hoặc perl
khi được gọi với -i
cờ ("tại chỗ"), hoàn toàn không thực sự "tại chỗ". Nó nên được gọi là "nơi mới với tên cũ".
Điều này hoạt động tốt bởi vì unix đảm bảo (ít nhất là đối với các hệ thống tệp cục bộ) rằng một tệp đã mở tiếp tục tồn tại cho đến khi nó bị đóng, ngay cả khi nó bị "xóa" và một tệp mới có cùng tên được tạo. (Không phải ngẫu nhiên mà hệ thống unix gọi "xóa" một tệp thực sự được gọi là "hủy liên kết".) Vì vậy, nói chung, nếu trình thông dịch shell có một số tệp nguồn mở và bạn "chỉnh sửa" tệp theo cách được mô tả ở trên , shell thậm chí sẽ không thấy các thay đổi vì nó vẫn mở tệp gốc.
[Lưu ý: như với tất cả các nhận xét dựa trên tiêu chuẩn, phần trên có thể diễn giải theo nhiều cách hiểu và có nhiều trường hợp góc khác nhau, chẳng hạn như NFS. Trẻ em được chào đón để điền vào các ý kiến với các ngoại lệ.]
Tất nhiên, có thể sửa đổi các tập tin trực tiếp; Nó không thuận tiện cho mục đích chỉnh sửa, bởi vì trong khi bạn có thể ghi đè lên dữ liệu trong một tệp, bạn không thể xóa hoặc chèn mà không thay đổi tất cả dữ liệu sau, điều này có nghĩa là sẽ viết lại khá nhiều. Hơn nữa, trong khi bạn đang thực hiện việc dịch chuyển đó, nội dung của tệp sẽ không thể đoán trước được và các quá trình mở tệp sẽ bị ảnh hưởng. Để thoát khỏi điều này (ví dụ như với các hệ thống cơ sở dữ liệu), bạn cần một bộ giao thức sửa đổi tinh vi và các khóa phân tán; những thứ vượt ra ngoài phạm vi của một tiện ích chỉnh sửa tập tin điển hình.
Vì vậy, nếu bạn muốn chỉnh sửa một tệp trong khi nó được xử lý bởi trình bao, bạn có hai tùy chọn:
Bạn có thể nối vào tập tin. Điều này nên luôn luôn làm việc.
Bạn có thể ghi đè lên tệp có nội dung mới có cùng độ dài . Điều này có thể hoặc không thể hoạt động, tùy thuộc vào việc shell đã đọc phần đó của tệp hay chưa. Vì hầu hết các tệp I / O liên quan đến bộ đệm đọc và vì tất cả các shell mà tôi biết đều đọc toàn bộ lệnh ghép trước khi thực hiện nó, nên rất khó để bạn có thể thoát khỏi điều này. Nó chắc chắn sẽ không đáng tin cậy.
Tôi không biết bất kỳ từ ngữ nào trong tiêu chuẩn Posix thực sự đòi hỏi khả năng nối thêm tệp tập lệnh trong khi tệp đang được thực thi, do đó, nó có thể không hoạt động với mọi trình bao tuân thủ Posix, ít hơn nhiều với việc cung cấp gần như hiện tại và vỏ đôi khi-posix. Vậy YMMV. Nhưng theo như tôi biết, nó hoạt động đáng tin cậy với bash.
Bằng chứng là đây là một triển khai "không có vòng lặp" của chương trình 99 chai bia khét tiếng trong bash, sử dụng dd
để ghi đè và nối thêm (phần ghi đè có lẽ an toàn vì nó thay thế dòng hiện đang thực thi, luôn là dòng cuối cùng của dòng tệp, với một nhận xét có cùng độ dài; tôi đã làm điều đó để kết quả cuối cùng có thể được thực thi mà không có hành vi tự sửa đổi.)
#!/bin/bash
if [[ $1 == reset ]]; then
printf "%s\n%-16s#\n" '####' 'next ${1:-99}' |
dd if=/dev/stdin of=$0 seek=$(grep -bom1 ^#### $0 | cut -f1 -d:) bs=1 2>/dev/null
exit
fi
step() {
s=s
one=one
case $beer in
2) beer=1; unset s;;
1) beer="No more"; one=it;;
"No more") beer=99; return 1;;
*) ((--beer));;
esac
}
next() {
step ${beer:=$(($1+1))}
refrain |
dd if=/dev/stdin of=$0 seek=$(grep -bom1 ^next\ $0 | cut -f1 -d:) bs=1 conv=notrunc 2>/dev/null
}
refrain() {
printf "%-17s\n" "# $beer bottles"
echo echo ${beer:-No more} bottle$s of beer on the wall, ${beer:-No more} bottle$s of beer.
if step; then
echo echo Take $one down, pass it around, $beer bottle$s of beer on the wall.
echo echo
echo next abcdefghijkl
else
echo echo Go to the store, buy some more, $beer bottle$s of beer on the wall.
fi
}
####
next ${1:-99} #