J'écrivais un script bash et j'ai mis à jour le code (enregistré le fichier de script sur le disque) pendant que le script attendait une entrée dans une while
boucle. Après être revenu sur le terminal et continuer avec l'appel précédent du script, bash a donné une erreur sur la syntaxe du fichier:
/home/aularon/bin/script: line 58: unexpected EOF while looking for matching `"'
/home/aularon/bin/script: line 67: syntax error: unexpected end of file
J'ai donc essayé de faire ce qui suit:
1er: créez un script, self-update.sh
appelons-le:
#!/bin/bash
fname=$(mktemp)
cat $0 | sed 's/BEFORE\./AFTER!./' > $fname
cp $fname $0
rm -f $fname
echo 'String: BEFORE.';
Le script lit son code, change le mot «AVANT» en «APRÈS», puis se réécrit avec le nouveau code.
2ème Run it:
chmod +x self-update.sh
./self-update.sh
3ème merveille ...
aularon@aularon-laptop:~$ ./self-update.sh
String: AFTER!.
Maintenant, je n'aurais pas deviné que sur la même invocation, il sortirait APRÈS! , sur la deuxième manche, bien sûr, mais pas sur la première.
Ma question est donc: est-ce intentionnel (par conception)? ou c'est à cause de la façon dont bash exécute le script? Ligne par ligne ou commande par commande. Y a-t-il une bonne utilisation d'un tel comportement? Un exemple?
Edit: J'ai essayé de reformater le fichier pour mettre toutes les commandes sur une seule ligne, cela ne fonctionne pas maintenant:
#!/bin/bash
fname=$(mktemp);cat $0 | sed 's/BEFORE\./AFTER!./' > $fname;cp $fname $0;rm -f $fname;echo 'String: BEFORE.';
Production:
aularon@aularon-laptop:~$ ./self-update.sh #First invocation
String: BEFORE.
aularon@aularon-laptop:~$ ./self-update.sh #Second invocation
String: AFTER!.
Lors du déplacement de la echo
chaîne vers la ligne suivante, en la séparant de l' cp
appel rewriting ( ):
#!/bin/bash
fname=$(mktemp);cat $0 | sed 's/BEFORE\./AFTER!./' > $fname;cp $fname $0;rm -f $fname;
echo 'String: BEFORE.';
Et maintenant ça marche à nouveau:
aularon@aularon-laptop:~$ ./self-update.sh
String: AFTER!.
Réponses:
C'est par conception. Bash lit les scripts en morceaux. Il va donc lire une partie du script, exécuter toutes les lignes qu'il peut, puis lire le morceau suivant.
Vous rencontrez donc quelque chose comme ceci:
Lorsque cela devient encore plus problématique, c'est que si vous modifiez quelque chose avant l'octet 256. Disons que vous supprimez quelques lignes. Ensuite, les données dans le script qui était à l'octet 256, sont maintenant ailleurs, disons à l'octet 156 (100 octets plus tôt). Pour cette raison, lorsque bash continuera à lire, il obtiendra ce qui était à l'origine 356.
C'est juste un exemple. Bash ne lit pas nécessairement 256 octets à la fois. Je ne sais pas exactement combien il lit à la fois, mais cela n'a pas d'importance, le comportement est toujours le même.
la source
stat
le fichier pour voir s'il a changé. Pas d'lseek
appels.echo foo
changé en aecho bar
pendantsleep
. Il se comporte comme ça depuis les versions 2, donc je ne pense pas que ce soit un problème de version.