Sous Unix, la plupart des éditeurs travaillent en créant un nouveau fichier temporaire contenant le contenu modifié. Lorsque le fichier modifié est enregistré, le fichier d'origine est supprimé et le fichier temporaire renommé avec le nom d'origine. (Il existe, bien sûr, diverses protections pour empêcher la perte de données.) Il s'agit, par exemple, du style utilisé par sed
ou perl
lorsqu'il est invoqué avec le -i
drapeau ("en place"), qui n'est pas vraiment "en place" du tout. Il aurait dû être appelé "nouvel endroit avec un ancien nom".
Cela fonctionne bien car unix garantit (au moins pour les systèmes de fichiers locaux) qu'un fichier ouvert continue d'exister jusqu'à sa fermeture, même s'il est "supprimé" et qu'un nouveau fichier portant le même nom est créé. (Ce n'est pas une coïncidence si l'appel système Unix pour "supprimer" un fichier est en fait appelé "délier".) Donc, de manière générale, si un interpréteur de shell a un fichier source ouvert et que vous "éditez" le fichier de la manière décrite ci-dessus , le shell ne verra même pas les modifications car il a toujours le fichier d'origine ouvert.
[Remarque: comme pour tous les commentaires normalisés, ce qui précède est sujet à de multiples interprétations et il existe différents cas d'angle, tels que NFS. Les pédants sont invités à remplir les commentaires avec des exceptions.]
Il est bien sûr possible de modifier directement des fichiers; ce n'est tout simplement pas très pratique à des fins d'édition, car même si vous pouvez écraser des données dans un fichier, vous ne pouvez pas supprimer ou insérer sans déplacer toutes les données suivantes, ce qui impliquerait beaucoup de réécriture. De plus, pendant que vous effectuiez ce déplacement, le contenu du fichier serait imprévisible et les processus qui avaient ouvert le fichier en souffriraient. Pour vous en sortir (comme avec les systèmes de base de données, par exemple), vous avez besoin d'un ensemble sophistiqué de protocoles de modification et de verrous distribués; des choses qui sont bien au-delà de la portée d'un utilitaire d'édition de fichiers typique.
Donc, si vous souhaitez éditer un fichier en cours de traitement par un shell, vous avez deux options:
Vous pouvez ajouter au fichier. Cela devrait toujours fonctionner.
Vous pouvez remplacer le fichier par un nouveau contenu exactement de la même longueur . Cela peut ou non fonctionner, selon que le shell a déjà lu cette partie du fichier ou non. Étant donné que la plupart des E / S de fichiers impliquent des tampons de lecture et que tous les shells que je connais lisent une commande composée entière avant de l'exécuter, il est peu probable que vous puissiez vous en sortir. Ce ne serait certainement pas fiable.
Je ne connais aucun libellé dans la norme Posix qui nécessite en fait la possibilité de l'ajouter à un fichier de script pendant que le fichier est en cours d'exécution, donc cela pourrait ne pas fonctionner avec chaque shell compatible Posix, et encore moins avec l'offre actuelle de presque- et des coques parfois compatibles posix. Alors YMMV. Mais pour autant que je sache, cela fonctionne de manière fiable avec bash.
Pour preuve, voici une implémentation "sans boucle" du fameux programme de 99 bouteilles de bière en bash, qui utilise dd
pour écraser et ajouter (l'écrasement est probablement sûr car il remplace la ligne en cours d'exécution, qui est toujours la dernière ligne de la fichier, avec un commentaire exactement de la même longueur; je l'ai fait pour que le résultat final puisse être exécuté sans le comportement d'auto-modification.)
#!/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} #