Modifica lo script di shell mentre è in esecuzione


91

È possibile modificare uno script di shell mentre è in esecuzione e le modifiche influiscono sullo script in esecuzione?

Sono curioso del caso specifico di uno script csh che ho quel batch che esegue una serie di versioni di build diverse e viene eseguito tutta la notte. Se mi viene in mente qualcosa durante l'operazione, vorrei entrare e aggiungere ulteriori comandi o commentare quelli non eseguiti.

Se non è possibile, esiste una shell o un meccanismo batch che mi consenta di farlo?

Certo che l'ho provato, ma passeranno ore prima di vedere se ha funzionato o no, e sono curioso di sapere cosa sta succedendo o meno dietro le quinte.


1
Ho visto due risultati dalla modifica del file di script per uno script in esecuzione: 1) le modifiche vengono ignorate come se avesse letto tutto in memoria o 2) lo script si blocca con un errore come se avesse letto parte del comando. Non so se dipende dalle dimensioni dello script. Ad ogni modo, non lo proverei.
Paul Tomblin

In breve: no, a meno che non sia autoreferenziale / chiamante, nel qual caso lo script principale sarebbe ancora quello vecchio.
Wrikken

Ci sono due domande importanti qui. 1) Come posso aggiungere correttamente e in sicurezza comandi a uno script in esecuzione? 2) Quando modifico uno script in esecuzione, cosa succede?
Chris Quenelle

3
La domanda è se una shell esegue uno script leggendo l'intero file di script e quindi eseguendolo, o leggendolo parzialmente durante l'esecuzione. Non so quale sia; potrebbe anche non essere specificato. Dovresti evitare di dipendere da entrambi i comportamenti.
Keith Thompson

Risposte:


-67

Gli script non funzionano in questo modo; la copia in esecuzione è indipendente dal file di origine che stai modificando. La prossima volta che lo script verrà eseguito, sarà basato sulla versione salvata più di recente del file di origine.

Potrebbe essere saggio suddividere questo script in più file ed eseguirli singolarmente. Ciò ridurrà il tempo di esecuzione fino al fallimento. (cioè, dividere il batch in uno script di build di sapori, eseguendo ciascuno individualmente per vedere quale sta causando il problema).


66
Ho osservato l'opposto. L'esecuzione di script bash che vengono modificati può causare l'arresto anomalo dello script in esecuzione perché il file sembra spostarsi nella posizione del file di lettura dello script di bash.
Tilman Vogel

10
Nella mia esperienza su più sistemi, la copia in esecuzione NON è indipendente dal file su disco, ecco perché questo problema è così sorprendente e importante nella programmazione di script di shell.
Chris Quenelle

6
Sicuramente non è indipendente dal file su disco. La shell di solito legge gli script in blocchi, ad esempio, di 128 byte o 4096 byte o 16384 byte, e legge il blocco successivo solo quando ha bisogno di un nuovo input. (Puoi fare cose come lsof su una shell che esegue uno script e vedere che il file è ancora aperto.)
mirabilos

5
No. In realtà, se modifichi uno script, il processo fallisce.
Erik Aronesty

8
Non hai ragione. Viene memorizzato nel buffer a seconda dell'implementazione e del comando effettivo chiamato nello script, indipendentemente dal fatto che stdout venga reindirizzato a un file, ci sono molti fattori e la tua risposta non è semplicemente corretta.
GL2014

50

Essa ha effetto, a bash almeno nel mio ambiente, ma in modo molto sgradevole . Vedi questi codici. Primo 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."

Fare

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

Nel mio caso, l'output è sempre:

Ciao
Ciao
È tutto.
È tutto.

(Ovviamente è molto meglio automatizzarlo, ma l'esempio sopra è leggibile.)

[modifica] Questo è imprevedibile, quindi pericoloso. La soluzione migliore è , come descritto qui, mettere tutto in una parentesi graffa e, prima della parentesi graffa di chiusura, mettere "exit" . Leggi bene la risposta collegata per evitare insidie.

[aggiunto] Il comportamento esatto dipende da una nuova riga in più, e forse anche dal tuo gusto Unix, filesystem, ecc. Se vuoi semplicemente vedere alcune influenze, aggiungi semplicemente "echo foo / bar" a b.sh prima e / o dopo la riga "leggi".


3
Mh, non vedo l'affetto. Mi sto perdendo qualcosa?
utente sconosciuto

Il comportamento esatto dipende da una nuova riga in più , e forse anche dal gusto Unix, dal filesystem, ecc., Pensato per niente sicuro. Se vuoi semplicemente vedere le influenze, ingrandisci semplicemente b.shaggiungendo 10 linee di echo foo / bar / baz. L'essenza delle risposte di me e dave4220 è che l'effetto non è facile da prevedere. (A proposito, il sostantivo "affetto" significa "amore" =)
teika kazura,

sì, è molto rotto. ho una soluzione (sotto). ciò che è ancora più pericoloso sono gli aggiornamenti svn / rsync / git
Erik Aronesty

39

Prova questo ... crea un file chiamato bash-is-odd.sh:

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

Ciò dimostra che bash sta, effettivamente, interpretando lo script "mentre vai". In effetti, la modifica di uno script di lunga durata ha risultati imprevedibili, l'inserimento di caratteri casuali ecc. Perché? Poiché bash legge dalla posizione dell'ultimo byte, la modifica sposta la posizione del carattere corrente che viene letto.

Bash è, in una parola, molto, molto pericoloso a causa di questa "caratteristica". svn e rsyncquando vengono usati con gli script bash sono particolarmente problematici, perché per impostazione predefinita "uniscono" i risultati ... modificando sul posto. rsyncha una modalità che risolve questo problema. svn e git non lo fanno.

Presento una soluzione. Crea un file chiamato /bin/bashx:

#!/bin/bash
source "$1"

Ora usa #!/bin/bashxsui tuoi script ed eseguili sempre con bashxinvece di bash. Questo risolve il problema: puoi tranquillamente i rsynctuoi script.

Soluzione alternativa (in linea) proposta / testata da @ AF7:

{
   # your script
} 
exit $?

Le parentesi graffe proteggono dalle modifiche e l'uscita protegge dalle aggiunte. Ovviamente, staremmo tutti molto meglio se bash avesse un'opzione, come -w(intero file), o qualcosa che ha fatto questo.


1
Btw; ecco un vantaggio per contrastare il meno e perché mi piace la tua risposta modificata.
Andrew Barber

1
Non posso raccomandarlo. In questa soluzione alternativa, i parametri posizionali vengono spostati di uno. Ricorda inoltre che non puoi assegnare un valore a $ 0. Significa che se si cambia semplicemente "/ bin / bash" in "/ bin / bashx", molti script falliscono.
teika kazura

1
Per favore dimmi che tale opzione è già stata implementata!
AF7

10
Una soluzione semplice, suggeritami dal mio amico Giulio (crediti dove dovuti) è inserire {all'inizio e} alla fine dello scritp. Bash è costretto a leggere tutto in memoria.
AF7

1
@ AF7 migliorando la soluzione del tuo amico: {your_code; } && Uscita; impedirà che anche le righe aggiunte alla fine vengano eseguite.
korkman

17

Suddividi il tuo script in funzioni e ogni volta che viene chiamata una funzione, questa viene sourceda un file separato. Quindi puoi modificare i file in qualsiasi momento e lo script in esecuzione raccoglierà le modifiche la prossima volta che verrà acquisito.

foo() {
  source foo.sh
}
foo

Sto usando questa tecnica in modo efficace da un po 'di tempo per aggiornare i miei script di build di lunga durata mentre sono in esecuzione. Mi piacerebbe imparare una tecnica per far leggere il file corrente fino alla fine del file, in modo da non dover avere due file per implementare ogni script di shell.
Chris Quenelle

3

Buona domanda! Spero che questo semplice script aiuti

#!/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"

In Linux sembra che le modifiche apportate a un .sh in esecuzione vengano applicate dallo script in esecuzione, se puoi digitare abbastanza velocemente!


2

Una nota a margine interessante: se stai eseguendo uno script Python, non cambia. (Questo è probabilmente palesemente ovvio per chiunque capisca come la shell esegue gli script Python, ma ha pensato che potrebbe essere un utile promemoria per chi cerca questa funzionalità.)

Ho creato:

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

Quindi in un'altra shell, mentre questo è in sospensione, modifica l'ultima riga. Al termine, visualizza la riga inalterata, presumibilmente perché esegue un .pyc? Lo stesso accade su Ubuntu e macOS.


1

Non ho installato csh, ma

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

Eseguilo, modifica rapidamente l'ultima riga da leggere

echo Change happened

L'output è

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

Hrmph.

Immagino che le modifiche agli script della shell non abbiano effetto finché non vengono rieseguite.


2
dovresti mettere la stringa che vuoi mostrare tra virgolette.
user1463308

2
in realtà, dimostra che il tuo editor non funziona come pensi. molti, molti editor (inclusi vim, emacs) operano su un file "tmp" e non sul file live. Prova a usare "echo 'echo uh oh' >> myshell.sh" invece di vi / emacs ... e osserva come mostra il nuovo materiale. Peggio ancora ... svn e rsync modificano anche in questo modo!
Erik Aronesty

3
-1. Questo errore non è correlato al file che viene modificato: è perché stai usando un apostrofo! Funziona come una singola citazione, causando l'errore. Metti l'intera stringa tra virgolette e riprova.
Anonymous Penguin

5
Il fatto che si sia verificato l'errore mostra che la modifica non ha avuto l'effetto desiderato.
danmcardle

@danmcardle Chi lo sa? Forse bash ha visto Change didn'ned.
Kirill Bulygin

1

Se è tutto in un unico script, no, non funzionerà. Tuttavia, se lo imposti come uno script del driver che richiama i sub-script, potresti essere in grado di modificare un sub-script prima che venga chiamato, o prima che venga chiamato di nuovo se stai eseguendo il loop, e in tal caso credo che quei cambiamenti si rifletterebbe nell'esecuzione.


0

Ho sentito no ... ma che dire con qualche indiretto:

BatchRunner.sh

Command1.sh
Command2.sh

Command1.sh

runSomething

Command2.sh

runSomethingElse

Quindi dovresti essere in grado di modificare il contenuto di ogni file di comando prima che BatchRunner lo raggiunga, giusto?

O

Una versione più pulita avrebbe BatchRunner guardare a un singolo file dove sarebbe eseguito consecutivamente una riga alla volta. Quindi dovresti essere in grado di modificare questo secondo file mentre il primo è in esecuzione, giusto?


Mi chiedo se li carica in memoria per eseguirli e una modifica non ha importanza una volta avviato il processo principale ...
Eric Hodonsky

0

Usa invece Zsh per il tuo script.

AFAICT, Zsh non mostra questo comportamento frustrante.


Questa è la ragione # 473 per preferire Zsh a bash. Recentemente ho lavorato su un vecchio script bash che richiede 10 minuti per essere eseguito e non posso modificarlo mentre aspetto il completamento!
Micah Elliott

-5

di solito, non è comune modificare lo script mentre è in esecuzione. Tutto quello che devi fare è mettere sotto controllo le tue operazioni. Usa le istruzioni if ​​/ else per verificare le condizioni. Se qualcosa fallisce, allora fallo, altrimenti fallo. Questa è la strada da percorrere.


In realtà si tratta meno del fallimento degli script che della decisione di modificare il lavoro batch durante l'operazione. IE capendo che c'è di più che voglio compilare, o che non ho bisogno di determinati lavori già in coda.
ack

1
Se aggiungi rigorosamente agli script, bash farà quello che ti aspetti!
Erik Aronesty
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.