Edite el script de shell mientras se está ejecutando

91

¿Puede editar un script de shell mientras se está ejecutando y que los cambios afecten al script en ejecución?

Tengo curiosidad sobre el caso específico de un script csh que tengo, ese lote ejecuta un montón de versiones de compilación diferentes y se ejecuta toda la noche. Si se me ocurre algo en mitad de la operación, me gustaría ingresar y agregar comandos adicionales, o comentar los que no se han ejecutado.

Si no es posible, ¿existe algún mecanismo de shell o por lotes que me permita hacer esto?

Por supuesto que lo he probado, pero pasarán horas antes de que vea si funcionó o no, y tengo curiosidad por saber qué está pasando o no detrás de escena.

ack
fuente
1
He visto dos resultados al editar el archivo de script para un script en ejecución: 1) los cambios se ignoran como si hubiera leído todo en la memoria o 2) el script falla con un error como si hubiera leído parte del comando. No sé si eso depende del tamaño del guión. De cualquier manera, no lo intentaría.
Paul Tomblin
En resumen: no, a menos que sea autorreferencial / llamada, en cuyo caso el script principal seguirá siendo el anterior.
Wrikken
Aquí hay dos preguntas importantes. 1) ¿Cómo puedo agregar comandos de manera correcta y segura a un script en ejecución? 2) Cuando modifico un script en ejecución, ¿qué sucederá?
Chris Quenelle
3
La pregunta es si un shell ejecuta un script leyendo el archivo de script completo y luego ejecutándolo, o leyéndolo parcialmente mientras se ejecuta. No sé cuál es; puede que ni siquiera se especifique. Debe evitar depender de cualquiera de los dos comportamientos.
Keith Thompson

Respuestas:

-67

Los scripts no funcionan de esa manera; la copia en ejecución es independiente del archivo fuente que está editando. La próxima vez que se ejecute el script, se basará en la versión guardada más recientemente del archivo fuente.

Puede ser conveniente dividir este script en varios archivos y ejecutarlos individualmente. Esto reducirá el tiempo de ejecución hasta el fallo. (es decir, divida el lote en scripts de tipo de compilación, ejecutando cada uno individualmente para ver cuál está causando el problema).

vado
fuente
66
He observado lo contrario. La ejecución de scripts de bash que se editan puede hacer que el script en ejecución se bloquee porque el archivo parece moverse debajo de la posición del archivo de lectura de script de bash.
Tilman Vogel
10
En mi experiencia en múltiples sistemas, la copia en ejecución NO es independiente del archivo de disco, por eso este problema es tan sorprendente e importante en la programación de scripts de shell.
Chris Quenelle
6
Definitivamente no es independiente del archivo en disco. El shell normalmente lee los scripts en bloques de, por ejemplo, 128 bytes o 4096 bytes o 16384 bytes, y solo lee el siguiente bloque cuando necesita una nueva entrada. (Puede hacer cosas como lsof en un shell ejecutando un script y ver que todavía tiene el archivo abierto)
Mirabilos
5
No. En realidad, si edita un script, el proceso falla.
Erik Aronesty
8
No tienes razón. Se almacena en búfer según la implementación y el comando real que se llama en el script, ya sea que stdout se redirija a un archivo, hay muchos factores y su respuesta no es simplemente correcta.
GL2014
50

Que no afecta, al menos fiesta en mi entorno, pero en forma muy desagradable . Vea estos códigos. Primero 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."

Hacer

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

En mi caso, la salida es siempre:

Hola
Hola
Eso es todo.
Eso es todo.

(Por supuesto, es mucho mejor automatizarlo, pero el ejemplo anterior es legible).

[editar] Esto es impredecible, por lo tanto peligroso. La mejor solución es , como se describe aquí, poner todo en una llave, y antes de la llave de cierre, poner "salida" . Lea bien la respuesta vinculada para evitar errores.

[agregado] El comportamiento exacto depende de una nueva línea adicional, y quizás también de su versión Unix, sistema de archivos, etc. Si simplemente desea ver algunas influencias, simplemente agregue "echo foo / bar" a b.sh antes y / o después la línea "leer".

teika kazura
fuente
3
Mh, no veo el afecto. ¿Me estoy perdiendo de algo?
usuario desconocido
El comportamiento exacto depende de una nueva línea adicional , y quizás también de la versión Unix, el sistema de archivos, etc., aunque no estoy seguro en absoluto. Si simplemente desea ver alguna influencia, simplemente amplíe b.shagregando 10 líneas de eco foo / bar / baz. La esencia de las respuestas de dave4220 y yo es que el efecto no es fácil de predecir. (Por cierto, el sustantivo "afecto" significa "amor" =)
teika kazura
sí, está muy roto. tengo una solución (abajo). lo que es aún más peligroso son las actualizaciones de svn / rsync / git
Erik Aronesty
39

Prueba esto ... crea un archivo llamado bash-is-odd.sh:

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

Eso demuestra que bash está, de hecho, interpretando el guión "sobre la marcha". De hecho, editar un script de larga duración tiene resultados impredecibles, inserta caracteres aleatorios, etc. ¿Por qué? Debido a que bash lee desde la última posición del byte, la edición cambia la ubicación del carácter actual que se está leyendo.

Bash es, en una palabra, muy, muy inseguro debido a esta "característica". svn y rsynccuando se usan con scripts bash son particularmente preocupantes, porque por defecto "fusionan" los resultados ... editando en su lugar. rsynctiene un modo que corrige esto. svn y git no.

Presento una solución. Crea un archivo llamado /bin/bashx:

#!/bin/bash
source "$1"

Ahora úselo #!/bin/bashxen sus scripts y ejecútelos siempre con en bashxlugar de bash. Esto soluciona el problema: puede utilizar rsyncsus scripts de forma segura .

Solución alternativa (en línea) propuesta / probada por @ AF7:

{
   # your script
} 
exit $?

Los tirantes rizados protegen contra ediciones y la salida protege contra anexos. Por supuesto, todos estaríamos mucho mejor si bash viniera con una opción, como -w(archivo completo), o algo que hiciera esto.

Erik Aronesty
fuente
1
Por cierto; aquí hay un plus para contrarrestar el menos y porque me gusta su respuesta editada.
Andrew Barber
1
No puedo recomendar esto. En esta solución, los parámetros posicionales se cambian en uno. Recuerde también que no puede asignar un valor a $ 0. Significa que si simplemente cambia "/ bin / bash" a "/ bin / bashx", muchos scripts fallan.
teika kazura
1
Por favor, dígame que esta opción ya se implementó.
AF7
10
Una solución simple, que me sugirió mi amigo Giulio (créditos donde se deben) es insertar {al principio y} al final del scritp. Bash se ve obligado a leer todo lo que tiene en la memoria.
AF7
1
@ AF7 mejorando la solución de su amigo: {your_code; } && salida; evitará que las líneas agregadas al final también se ejecuten.
korkman
17

Divida su secuencia de comandos en funciones, y cada vez que se llame a una función, lo haga sourcedesde un archivo separado. Luego, puede editar los archivos en cualquier momento y su secuencia de comandos en ejecución recogerá los cambios la próxima vez que se obtenga.

foo() {
  source foo.sh
}
foo
Glenn Jackman
fuente
He estado usando esta técnica de manera efectiva durante un tiempo para actualizar mis scripts de compilación de larga ejecución mientras se ejecutan. Me encantaría aprender una técnica para hacer que el archivo actual se lea hasta el final del archivo, de modo que no tenga que tener dos archivos para implementar cada script de shell.
Chris Quenelle
3

¡Buena pregunta! Espero que este sencillo script ayude

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

En Linux, parece que los cambios realizados en un .sh en ejecución son ejecutados por el script en ejecución, ¡si puede escribir lo suficientemente rápido!

Will Turner
fuente
2

Una nota al margen interesante: si está ejecutando un script de Python, no cambia. (Esto probablemente sea descaradamente obvio para cualquiera que entienda cómo el shell ejecuta los scripts de Python, pero pensó que podría ser un recordatorio útil para alguien que busque esta funcionalidad).

Yo creé:

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

Luego, en otro shell, mientras está inactivo, edite la última línea. Cuando esto se completa, muestra la línea inalterada, presumiblemente porque está ejecutando un .pyc? Lo mismo sucede en Ubuntu y macOS.

Chris
fuente
1

No tengo csh instalado, pero

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

Ejecuta eso, edita rápidamente la última línea para leer

echo Change happened

La salida es

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

Hrmph.

Supongo que las ediciones de los scripts de shell no tienen efecto hasta que se vuelven a ejecutar.

dave4420
fuente
2
debe poner la cadena que desea mostrar entre comillas.
user1463308
2
de hecho, demuestra que su editor no funciona de la manera que piensa. muchos, muchos editores (incluidos vim, emacs) operan en un archivo "tmp" y no en el archivo en vivo. Intente usar "echo 'echo uh oh' >> myshell.sh" en lugar de vi / emacs ... y observe como genera las cosas nuevas. Peor ... ¡svn y rsync también editan de esta manera!
Erik Aronesty
3
-1. Ese error no está relacionado con el archivo que se está editando: ¡es porque estás usando un apóstrofe! Eso actúa como una comilla simple, provocando el error. Pon toda esa cadena entre comillas dobles y vuelve a intentarlo.
Pingüino anónimo
5
El hecho de que ocurrió el error muestra que la edición no tuvo el efecto deseado.
danmcardle
@danmcardle ¿Quién sabe? Quizás bash vio Change didn'ned.
Kirill Bulygin
1

Si todo esto está en un solo script, entonces no, no funcionará. Sin embargo, si lo configura como un script de controlador que llama a subguiones, es posible que pueda cambiar un subguión antes de que se llame, o antes de que se vuelva a llamar si está en bucle, y en ese caso creo que esos cambios se reflejaría en la ejecución.

Pastor Ethan
fuente
0

Escucho que no ... pero ¿qué pasa con algunas indirectas?

BatchRunner.sh

Command1.sh
Command2.sh

Command1.sh

runSomething

Command2.sh

runSomethingElse

Entonces, debería poder editar el contenido de cada archivo de comando antes de que BatchRunner llegue a él, ¿verdad?

O

Una versión más limpia haría que BatchRunner buscara un solo archivo donde se ejecutaría consecutivamente una línea a la vez. Entonces, debería poder editar este segundo archivo mientras el primero se está ejecutando, ¿verdad?

ack
fuente
Me pregunto si los carga en la memoria para ejecutarlos y un cambio no importa una vez que se inicia el proceso principal ...
Eric Hodonsky
0

Use Zsh en su lugar para sus scripts.

AFAICT, Zsh no exhibe este comportamiento frustrante.

Micah Elliott
fuente
Esta es la razón # 473 para preferir Zsh a bash. Recientemente he estado trabajando en un antiguo script de bash que tarda 10 m en ejecutarse, ¡y no puedo editarlo mientras espero que se complete!
Micah Elliott
-5

por lo general, es poco común editar su script mientras se está ejecutando. Todo lo que tiene que hacer es poner bajo control sus operaciones. Use declaraciones if / else para verificar las condiciones. Si algo falla, haz esto, de lo contrario haz aquello. Ese es el camino a seguir.

ghostdog74
fuente
En realidad, se trata menos de errores de scripts que de decidir modificar el trabajo por lotes en mitad de la operación. IE se da cuenta de que hay más que quiero compilar, o que no necesito ciertos trabajos que ya están en cola.
ack
1
Si agrega estrictamente a los scripts, ¡bash hará lo que espera!
Erik Aronesty