Habituellement, si vous modifiez un script, toutes les utilisations en cours du script sont sujettes à des erreurs.
Autant que je sache, bash (d'autres shells aussi?) Lit le script de manière incrémentielle, donc si vous modifiez le fichier de script en externe, il commence à lire les mauvaises choses. Existe-t-il un moyen de l'empêcher ?
Exemple :
sleep 20
echo test
Si vous exécutez ce script, bash lira la première ligne (disons 10 octets) et se mettra en veille. Lorsqu'il reprend, il peut y avoir différents contenus dans le script à partir du 10ème octet. Je suis peut-être au milieu d'une ligne dans le nouveau script. Ainsi, le script en cours d'exécution sera cassé.
Réponse acceptée :
Oui shells et bash
en particulier, veillez à lire le fichier une ligne à la fois, afin qu'il fonctionne de la même manière que lorsque vous l'utilisez de manière interactive.
Vous remarquerez que lorsque le fichier n'est pas recherchable (comme un tuyau), bash
lit même un octet à la fois pour être sûr de ne pas lire au-delà du \n
personnage. Lorsque le fichier est recherchable, il optimise en lisant des blocs complets à la fois, mais en revenant après le \n
.
Cela signifie que vous pouvez faire des choses comme :
bash << \EOF
read var
var's content
echo "$var"
EOF
Ou écrivez des scripts qui se mettent à jour. Ce que vous ne pourriez pas faire s'il ne vous donnait pas cette garantie.
Maintenant, il est rare que vous vouliez faire des choses comme ça et, comme vous l'avez découvert, cette fonctionnalité a tendance à vous gêner plus souvent qu'elle n'est utile.
Pour l'éviter, vous pouvez essayer de vous assurer de ne pas modifier le fichier sur place (par exemple, modifier une copie et déplacer la copie en place (comme sed -i
ou perl -pi
et certains éditeurs le font par exemple)).
Ou vous pouvez écrire votre script comme :
{
sleep 20
echo test
}; exit
(notez qu'il est important que la exit
être sur la même ligne que }
; bien que vous puissiez également le mettre à l'intérieur des accolades juste avant celle de fermeture).
ou :
main() {
sleep 20
echo test
}
main "[email protected]"; exit
Le shell devra lire le script jusqu'à la exit
avant de commencer à faire quoi que ce soit. Cela garantit que le shell ne lira plus le script.
Cela signifie que tout le script sera stocké en mémoire.
Cela peut également affecter l'analyse du script.
Par exemple, dans bash
:
export LC_ALL=fr_FR.UTF-8
echo $'St\ue9phane'
Sortirait ce U + 00E9 encodé en UTF-8. Cependant, si vous le changez en :
{
export LC_ALL=fr_FR.UTF-8
echo $'St\ue9phane'
}
Le \ue9
sera développé dans le jeu de caractères qui était en vigueur au moment où cette commande a été analysée, ce qui dans ce cas est avant le export
la commande est exécutée.
Notez également que si le source
alias .
est utilisée, avec certains shells, vous aurez le même genre de problème pour les fichiers sourcés.
Ce n'est pas le cas de bash
bien que dont la source
La commande lit entièrement le fichier avant de l'interpréter. Si vous écrivez pour bash
plus précisément, vous pourriez en faire usage, en ajoutant au début du script :
if [[ ! $already_sourced ]]; then
already_sourced=1
source "$0"; exit
fi
(Je ne compterais pas là-dessus, car vous pouvez imaginer les futures versions de bash
pourrait changer ce comportement qui peut actuellement être considéré comme une limitation (bash et AT&T ksh sont les seuls shells de type POSIX qui se comportent comme ça pour autant que je sache) et le already_sourced
l'astuce est un peu fragile car elle suppose que la variable n'est pas dans l'environnement, sans compter qu'elle affecte le contenu de la variable BASH_SOURCE)