Modifier le script shell pendant son exécution

91

Pouvez-vous modifier un script shell pendant son exécution et que les modifications affectent le script en cours d'exécution?

Je suis curieux de connaître le cas spécifique d'un script csh que j'ai, ce lot exécute un tas de saveurs de construction différentes et fonctionne toute la nuit. Si quelque chose me vient à l'esprit en cours d'opération, j'aimerais ajouter des commandes supplémentaires ou commenter celles qui n'ont pas été exécutées.

Si ce n'est pas possible, y a-t-il un shell ou un mécanisme de traitement par lots qui me permettrait de le faire?

Bien sûr, je l'ai essayé, mais il me faudra des heures avant de voir si cela a fonctionné ou non, et je suis curieux de savoir ce qui se passe ou ne se passe pas dans les coulisses.

accuser
la source
1
J'ai vu deux résultats de l'édition du fichier de script pour un script en cours d'exécution: 1) les modifications sont ignorées comme s'il avait lu le tout en mémoire ou 2) le script se bloque avec une erreur comme s'il avait lu une partie de la commande. Je ne sais pas si cela dépend de la taille du script. De toute façon, je n'essaierais pas.
Paul Tomblin
En bref: non, à moins que ce ne soit auto-référentiel / appelant, auquel cas le script principal serait toujours l'ancien.
Wrikken
Il y a ici deux questions importantes. 1) Comment puis-je ajouter correctement et en toute sécurité des commandes à un script en cours d'exécution? 2) Lorsque je modifie un script en cours d'exécution, que se passe-t-il?
Chris Quenelle
3
La question est de savoir si un shell exécute un script en lisant l'intégralité du fichier de script puis en l'exécutant, ou en le lisant partiellement lors de son exécution. Je ne sais pas de quoi il s'agit; il pourrait même ne pas être spécifié. Vous devriez éviter en fonction de l'un ou l'autre comportement.
Keith Thompson

Réponses:

-67

Les scripts ne fonctionnent pas de cette façon; la copie en cours d'exécution est indépendante du fichier source que vous éditez. La prochaine fois que le script sera exécuté, il sera basé sur la version la plus récemment enregistrée du fichier source.

Il peut être judicieux de diviser ce script en plusieurs fichiers et de les exécuter individuellement. Cela réduira le temps d'exécution à l'échec. (c'est-à-dire, divisez le lot en un script de build, en exécutant chacun individuellement pour voir lequel est à l'origine du problème).

gué
la source
66
J'ai observé le contraire. L'exécution de scripts bash qui sont modifiés peut entraîner le blocage du script en cours d'exécution car le fichier semble se déplacer sous la position du fichier de lecture du script de bash.
Tilman Vogel
10
D'après mon expérience sur plusieurs systèmes, la copie exécutée n'est PAS indépendante du fichier disque, c'est pourquoi ce problème est si surprenant et important dans la programmation de scripts shell.
Chris Quenelle
6
Ce n'est certainement pas indépendant du fichier sur disque. Le shell lit généralement les scripts par blocs de, par exemple, 128 octets ou 4096 octets ou 16384 octets, et ne lit que le bloc suivant lorsqu'il a besoin d'une nouvelle entrée. (Vous pouvez faire des choses comme lsof sur un shell exécutant un script et voir que le fichier est toujours ouvert.)
mirabilos
5
Non. En fait, si vous modifiez un script, le processus échoue.
Erik Aronesty
8
Vous n'avez pas raison. Il est mis en mémoire tampon en fonction de l'implémentation et de la commande réelle appelée dans le script, si stdout est redirigé vers un fichier, il existe de nombreux facteurs et votre réponse n'est pas simplement correcte.
GL2014
50

Il n'affecte, au moins bash dans mon environnement, mais de façon très désagréable . Voir ces codes. Premièrement :a.sh

#!/bin/sh

echo "First echo"
read y

echo "$y"

echo "That's all."

b.sh:

#!/bin/sh

echo "First echo"
read y

echo "Inserted"

echo "$y"

# echo "That's all."

Faire

$ cp a.sh run.sh
$ ./run.sh
$ # open another terminal
$ cp b.sh run.sh  # while 'read' is in effect
$ # Then type "hello."

Dans mon cas, la sortie est toujours:

Bonjour
Bonjour
C'est tout.
C'est tout.

(Bien sûr, il est préférable de l'automatiser, mais l'exemple ci-dessus est lisible.)

[modifier] Ceci est imprévisible, donc dangereux. La meilleure solution de contournement est , comme décrit ici, de tout mettre entre accolades, et avant l'accolade fermante, de mettre "exit" . Lisez bien la réponse liée pour éviter les pièges.

[ajouté] Le comportement exact dépend d'une nouvelle ligne supplémentaire, et peut-être aussi de votre saveur Unix, de votre système de fichiers, etc. Si vous voulez simplement voir quelques influences, ajoutez simplement "echo foo / bar" à b.sh avant et / ou après la ligne "lire".

teika kazura
la source
3
Mh, je ne vois pas l'affection. Est-ce que je manque quelque chose?
utilisateur inconnu le
Le comportement exact dépend d' une nouvelle ligne supplémentaire , et peut-être aussi de la saveur Unix, du système de fichiers, etc., je ne suis pas sûr du tout. Si vous voulez simplement voir des influences, agrandissez simplement b.shen ajoutant 10 lignes d'écho foo / bar / baz. L'essentiel des réponses de Dave4220 et moi est que l'effet n'est pas facile à prévoir. (BTW le nom «affection» signifie «amour» =)
teika kazura
oui, c'est très cassé. j'ai une solution (ci-dessous). ce qui est encore plus dangereux, ce sont les mises à jour svn / rsync / git
Erik Aronesty
39

Essayez ceci ... créez un fichier appelé bash-is-odd.sh:

#!/bin/bash
echo "echo yes i do odd things" >> bash-is-odd.sh

Cela démontre que bash interprète effectivement le script "au fur et à mesure". En effet, l'édition d'un script de longue durée a des résultats imprévisibles, l'insertion de caractères aléatoires etc. Pourquoi? Étant donné que bash lit à partir de la dernière position d'octet, l'édition décale l'emplacement du caractère en cours de lecture.

Bash est, en un mot, très, très dangereux à cause de cette "fonctionnalité". svn et rsynclorsqu'ils sont utilisés avec des scripts bash sont particulièrement gênants, car par défaut ils "fusionnent" les résultats ... l'édition en place. rsynca un mode qui résout ce problème. svn et git ne le font pas.

Je présente une solution. Créez un fichier appelé /bin/bashx:

#!/bin/bash
source "$1"

Utilisez maintenant #!/bin/bashxvos scripts et exécutez-les toujours avec bashxau lieu de bash. Cela résout le problème - vous pouvez en toute sécurité rsyncvos scripts.

Solution alternative (en ligne) proposée / testée par @ AF7:

{
   # your script
} 
exit $?

Les accolades bouclées protègent contre les modifications et la sortie protège contre les appendices. Bien sûr, nous serions tous beaucoup mieux si bash proposait une option, comme -w(fichier entier), ou quelque chose qui faisait cela.

Erik Aronesty
la source
1
Btw; voici un plus pour contrer le moins et parce que j'aime votre réponse éditée.
Andrew Barber
1
Je ne peux pas recommander ça. Dans cette solution de contournement, les paramètres de position sont décalés de un. Souvenez-vous également que vous ne pouvez pas attribuer une valeur à 0 $. Cela signifie que si vous remplacez simplement "/ bin / bash" par "/ bin / bashx", de nombreux scripts échouent.
teika kazura
1
Veuillez me dire qu'une telle option a déjà été implémentée!
AF7
10
Une solution simple, suggérée par mon ami Giulio (crédits là où il est dû), est d'insérer {au début et} à la fin du scritp. Bash est obligé de tout lire en mémoire.
AF7
1
@ AF7 améliore la solution de votre ami: {your_code; } && sortie; empêchera également les lignes ajoutées à la fin d'être exécutées.
korkman le
17

Décomposez votre script en fonctions, et chaque fois qu'une fonction vous est appelée, sourceelle provient d'un fichier séparé. Ensuite, vous pouvez modifier les fichiers à tout moment et votre script en cours d'exécution reprendra les modifications la prochaine fois qu'il sera récupéré.

foo() {
  source foo.sh
}
foo
Glenn Jackman
la source
J'utilise efficacement cette technique depuis un certain temps maintenant pour mettre à jour mes scripts de construction de longue durée pendant leur exécution. J'aimerais apprendre une technique pour faire lire le fichier actuel jusqu'à la fin du fichier, afin de ne pas avoir à avoir deux fichiers pour implémenter chaque script shell.
Chris Quenelle
3

Bonne question! J'espère que ce simple script aide

#!/bin/sh
echo "Waiting..."
echo "echo \"Success! Edits to a .sh while it executes do affect the executing script! I added this line to myself during execution\"  " >> ${0}
sleep 5
echo "When I was run, this was the last line"

Il semble que sous Linux, les modifications apportées à un .sh en cours d'exécution soient appliquées par le script en cours d'exécution, si vous pouvez taper assez vite!

Will Turner
la source
2

Une note latérale intéressante - si vous exécutez un script Python, cela ne change pas. (Ceci est probablement évident pour quiconque comprend comment le shell exécute les scripts Python, mais pense que cela pourrait être un rappel utile pour quelqu'un qui recherche cette fonctionnalité.)

J'ai créé:

#!/usr/bin/env python3
import time
print('Starts')
time.sleep(10)
print('Finishes unchanged')

Ensuite, dans un autre shell, pendant que celui-ci est en veille, modifiez la dernière ligne. Lorsque cela est terminé, il affiche la ligne inchangée, probablement parce qu'il exécute un .pyc? La même chose se produit sur Ubuntu et macOS.

Chris
la source
1

Je n'ai pas installé csh, mais

#!/bin/sh
echo Waiting...
sleep 60
echo Change didn't happen

Exécutez cela, modifiez rapidement la dernière ligne à lire

echo Change happened

La sortie est

Waiting...
/home/dave/tmp/change.sh: 4: Syntax error: Unterminated quoted string

Hrmph.

Je suppose que les modifications apportées aux scripts shell ne prennent effet qu'après leur réexécution.

dave4420
la source
2
vous devez mettre la chaîne que vous souhaitez afficher entre guillemets.
user1463308
2
en fait, cela prouve que votre éditeur ne fonctionne pas comme vous le pensez. de nombreux éditeurs (y compris vim, emacs) fonctionnent sur un fichier "tmp", et non sur le fichier live. Essayez d'utiliser "echo 'echo uh oh' >> myshell.sh" au lieu de vi / emacs ... et regardez comme il sort les nouvelles choses. Pire encore ... svn et rsync éditent également de cette façon!
Erik Aronesty
3
-1. Cette erreur n'est pas liée au fichier en cours d'édition: c'est parce que vous utilisez une apostrophe! Cela agit comme un guillemet simple, provoquant l'erreur. Mettez toute cette chaîne entre guillemets et réessayez.
Pingouin anonyme
5
Le fait que l'erreur se soit produite montre que la modification n'a pas eu l'effet escompté.
danmcardle
@danmcardle Qui sait? Peut-être que bash a vu Change didn'ned.
Kirill Bulygin
1

Si tout cela est dans un seul script, alors non, cela ne fonctionnera pas. Cependant, si vous le configurez en tant que script de pilote appelant des sous-scripts, vous pourrez peut-être modifier un sous-script avant qu'il ne soit appelé, ou avant qu'il ne soit appelé à nouveau si vous faites une boucle, et dans ce cas, je crois que ces changements serait reflété dans l'exécution.

Ethan Shepherd
la source
0

J'entends non ... mais qu'en est-il avec une indirection:

BatchRunner.sh

Command1.sh
Command2.sh

Command1.sh

runSomething

Command2.sh

runSomethingElse

Ensuite, vous devriez être en mesure de modifier le contenu de chaque fichier de commande avant que BatchRunner ne le fasse correctement?

OU

Une version plus propre permettrait à BatchRunner de regarder un seul fichier où il exécuterait consécutivement une ligne à la fois. Ensuite, vous devriez être en mesure de modifier ce deuxième fichier pendant que le premier fonctionne, n'est-ce pas?

accuser
la source
Je me demande si cela les charge en mémoire pour les exécuter et qu'un changement n'a pas d'importance une fois que le processus principal est lancé ...
Eric Hodonsky
0

Utilisez plutôt Zsh pour vos scripts.

AFAICT, Zsh ne présente pas ce comportement frustrant.

Micah Elliott
la source
C'est la raison # 473 de préférer Zsh à bash. J'ai récemment travaillé sur un ancien script bash qui prend 10m à exécuter, et je ne peux pas le modifier en attendant qu'il se termine!
Micah Elliott
-5

généralement, il est rare d'éditer votre script pendant son exécution. Tout ce que vous avez à faire est de mettre en contrôle vos opérations. Utilisez les instructions if / else pour vérifier les conditions. Si quelque chose échoue, faites ceci, sinon faites-le. C'est la voie à suivre.

ghostdog74
la source
Il s'agit en fait moins de l'échec des scripts que de la décision de modifier le travail par lots en cours d'opération. IE réalisant qu'il y a plus que je veux compiler, ou que je n'ai pas besoin de certains travaux déjà en file d'attente.
Ack
1
Si vous ajoutez strictement aux scripts, alors bash fera ce que vous attendez!
Erik Aronesty