W Uniksie większość edytorów pracuje, tworząc nowy plik tymczasowy zawierający edytowaną zawartość. Po zapisaniu edytowanego pliku oryginalny plik jest usuwany, a plik tymczasowy zostaje przemianowany na oryginalną nazwę. (Istnieją oczywiście różne zabezpieczenia zapobiegające utracie danych.) Jest to na przykład styl używany przez flagę („na miejscu”) sed
lub perl
wywoływany z nią -i
, co wcale nie jest tak naprawdę „na miejscu”. Powinno to zostać nazwane „nowym miejscem o starej nazwie”.
Działa to dobrze, ponieważ unix zapewnia (przynajmniej dla lokalnych systemów plików), że otwarty plik istnieje, dopóki nie zostanie zamknięty, nawet jeśli zostanie „usunięty” i utworzony zostanie nowy plik o tej samej nazwie. (To nie przypadek, że systemowe wywołanie unixa w celu „usunięcia” pliku nazywa się w rzeczywistości „unlink”.) Ogólnie rzecz biorąc, jeśli interpreter powłoki ma otwarty plik źródłowy, a plik „edytujesz” w sposób opisany powyżej , powłoka nawet nie zobaczy zmian, ponieważ nadal ma otwarty oryginalny plik.
[Uwaga: podobnie jak w przypadku wszystkich komentarzy opartych na standardach, powyższe podlega wielu interpretacjom i istnieją różne przypadki narożne, takie jak NFS. Zachęcamy do wypełniania komentarzy wyjątkami.]
Oczywiście można bezpośrednio modyfikować pliki; jest to po prostu mało wygodne do edycji, ponieważ chociaż możesz zastąpić dane w pliku, nie możesz go usunąć ani wstawić bez przesunięcia wszystkich następujących danych, co wymagałoby sporo przepisywania. Co więcej, podczas wykonywania tego przesunięcia zawartość pliku byłaby nieprzewidywalna i ucierpiałyby procesy, w których plik byłby otwarty. Aby tego uniknąć (na przykład w przypadku systemów baz danych), potrzebujesz zaawansowanego zestawu protokołów modyfikacji i rozproszonych blokad; rzeczy, które znacznie wykraczają poza zakres typowego narzędzia do edycji plików.
Jeśli więc chcesz edytować plik przetwarzany przez powłokę, masz dwie opcje:
Możesz dołączyć do pliku. To zawsze powinno działać.
Możesz zastąpić plik nową zawartością o dokładnie takiej samej długości . Może to działać lub nie, w zależności od tego, czy powłoka już odczytała tę część pliku, czy nie. Ponieważ większość operacji we / wy na plikach obejmuje bufory odczytu, a ponieważ wszystkie znane mi powłoki odczytują całą komendę złożoną przed jej wykonaniem, jest mało prawdopodobne, abyś mógł sobie z tym poradzić. Z pewnością nie byłby niezawodny.
Nie znam żadnego sformułowania w standardzie Posix, które faktycznie wymaga możliwości dołączenia do pliku skryptu podczas wykonywania pliku, więc może nie działać z każdą powłoką zgodną z Posix, a tym bardziej z obecną ofertą prawie- i czasami powłoki zgodne z posix. Więc YMMV. Ale o ile wiem, działa niezawodnie z bash.
Jako dowód, oto „pozbawiona pętli” implementacja niesławnego programu 99 butelek piwa w bash, który wykorzystuje dd
do nadpisywania i dołączania (nadpisywanie jest prawdopodobnie bezpieczne, ponieważ zastępuje aktualnie wykonywaną linię, która jest zawsze ostatnią linią plik z komentarzem o dokładnie tej samej długości; zrobiłem to, aby wynik końcowy mógł zostać wykonany bez zachowania samodmodyfikującego).
#!/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} #