In Unix erstellen die meisten Editoren eine neue temporäre Datei mit den bearbeiteten Inhalten. Beim Speichern der bearbeiteten Datei wird die Originaldatei gelöscht und die temporäre Datei in den Originalnamen umbenannt. (Es gibt natürlich verschiedene Sicherheitsvorkehrungen, um Datenverlust zu verhindern.) Dies ist beispielsweise der Stil, der von sed
oder perl
beim Aufrufen mit dem -i
Flag ("an Ort und Stelle") verwendet wird, das überhaupt nicht wirklich "an Ort und Stelle" ist. Es hätte "neuer Ort mit altem Namen" heißen sollen.
Dies funktioniert gut, da Unix (zumindest für lokale Dateisysteme) sicherstellt, dass eine geöffnete Datei weiterhin vorhanden ist, bis sie geschlossen wird, auch wenn sie "gelöscht" und eine neue Datei mit demselben Namen erstellt wird. (Es ist kein Zufall, dass der Unix-Systemaufruf zum "Löschen" einer Datei tatsächlich als "Verknüpfung aufheben" bezeichnet wird.) Wenn also in einem Shell-Interpreter eine Quelldatei geöffnet ist und Sie die Datei auf die oben beschriebene Weise "bearbeiten" sieht die Shell die Änderungen nicht einmal, da die Originaldatei noch geöffnet ist.
[Hinweis: Wie bei allen auf Standards basierenden Kommentaren unterliegt das Obige mehreren Interpretationen, und es gibt verschiedene Eckfälle, wie z. B. NFS. Pedanten sind herzlich eingeladen, die Kommentare mit Ausnahmen zu füllen.]
Es ist natürlich möglich, Dateien direkt zu ändern. Dies ist für Bearbeitungszwecke nicht besonders praktisch, da Sie zwar Daten in einer Datei überschreiben können, diese jedoch nicht löschen oder einfügen können, ohne alle folgenden Daten zu verschieben, was eine erhebliche Umschreibung zur Folge hätte. Außerdem wäre der Inhalt der Datei während dieser Verschiebung unvorhersehbar, und Prozesse, bei denen die Datei geöffnet war, würden darunter leiden. Um damit durchzukommen (wie zum Beispiel bei Datenbanksystemen), benötigen Sie einen ausgeklügelten Satz von Änderungsprotokollen und verteilten Sperren. Dinge, die weit über den Rahmen eines typischen Dateibearbeitungsprogramms hinausgehen.
Wenn Sie also eine Datei bearbeiten möchten, während sie von einer Shell verarbeitet wird, haben Sie zwei Möglichkeiten:
Sie können an die Datei anhängen. Das sollte immer funktionieren.
Sie können die Datei mit neuen Inhalten genau gleicher Länge überschreiben . Dies kann funktionieren oder auch nicht, je nachdem, ob die Shell diesen Teil der Datei bereits gelesen hat oder nicht. Da die meisten Datei-E / A-Vorgänge Lesepuffer beinhalten und alle mir bekannten Shells einen gesamten zusammengesetzten Befehl lesen, bevor sie ausgeführt werden, ist es ziemlich unwahrscheinlich, dass Sie damit durchkommen. Es wäre sicherlich nicht zuverlässig.
Ich kenne keine Formulierung im Posix-Standard, die tatsächlich das Anhängen einer Skriptdatei erfordert, während die Datei ausgeführt wird. Daher funktioniert sie möglicherweise nicht mit jeder Posix-kompatiblen Shell, geschweige denn mit dem aktuellen Angebot von fast und manchmal posix-konforme Schalen. Also YMMV. Aber meines Wissens funktioniert es zuverlässig mit bash.
Als Beweis ist hier eine "schleifenfreie" Implementierung des berüchtigten 99-Flaschen-Bier-Programms in bash, das dd
zum Überschreiben und Anhängen verwendet wird (das Überschreiben ist vermutlich sicher, da es die aktuell ausgeführte Zeile ersetzt, die immer die letzte Zeile der Datei, mit einem Kommentar von genau der gleichen Länge; ich habe das getan, damit das Endergebnis ohne das selbstmodifizierende Verhalten ausgeführt werden kann.)
#!/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} #