Tengo una pregunta general, que podría ser el resultado de una mala comprensión de cómo se manejan los procesos en Linux.
Para mis propósitos, voy a definir un 'script' como un fragmento de código bash guardado en un archivo de texto con permisos de ejecución habilitados para el usuario actual.
Tengo una serie de guiones que se llaman juntos. Por simplicidad, los llamaré guiones A, B y C. El guión A lleva a cabo una serie de declaraciones y luego hace una pausa, luego ejecuta el guión B, luego hace una pausa, luego ejecuta el guión C. En otras palabras, la serie de pasos es algo como esto:
Ejecute el script A:
- Serie de declaraciones
- Pausa
- Ejecutar script B
- Pausa
- Ejecutar script C
Sé por experiencia que si ejecuto el script A hasta la primera pausa, luego realizo ediciones en el script B, esas ediciones se reflejan en la ejecución del código cuando dejo que se reanude. Del mismo modo, si realizo modificaciones en el script C mientras el script A todavía está en pausa, luego permito que continúe después de guardar los cambios, esos cambios se reflejan en la ejecución del código.
Aquí está la verdadera pregunta, ¿hay alguna forma de editar el Script A mientras aún se está ejecutando? ¿O es imposible la edición una vez que comienza su ejecución?
fuente
Respuestas:
En Unix, la mayoría de los editores trabajan creando un nuevo archivo temporal que contiene los contenidos editados. Cuando se guarda el archivo editado, el archivo original se elimina y el archivo temporal se renombra al nombre original. (Por supuesto, existen varias salvaguardas para evitar daños). Este es, por ejemplo, el estilo utilizado por
sed
operl
cuando se invoca con la-i
bandera ("en el lugar"), que no está realmente "en el lugar". Debería haberse llamado "lugar nuevo con nombre antiguo".Esto funciona bien porque Unix asegura (al menos para los sistemas de archivos locales) que un archivo abierto continúa existiendo hasta que se cierra, incluso si se "elimina" y se crea un nuevo archivo con el mismo nombre. (No es casualidad que la llamada al sistema Unix para "eliminar" un archivo se llame realmente "desvincular"). Entonces, en términos generales, si un intérprete de shell tiene algún archivo fuente abierto y usted "edita" el archivo de la manera descrita anteriormente , el shell ni siquiera verá los cambios ya que todavía tiene abierto el archivo original.
[Nota: como con todos los comentarios basados en estándares, lo anterior está sujeto a múltiples interpretaciones y hay varios casos de esquina, como NFS. Los pendientes pueden llenar los comentarios con excepciones.]
Por supuesto, es posible modificar archivos directamente; simplemente no es muy conveniente para fines de edición, porque si bien puede sobrescribir datos en un archivo, no puede eliminarlos ni insertarlos sin cambiar todos los datos siguientes, lo que implicaría una gran cantidad de reescritura. Además, mientras realizaba ese cambio, el contenido del archivo sería impredecible y los procesos que tenían el archivo abierto sufrirían. Para salirse con la suya (como con los sistemas de bases de datos, por ejemplo), necesita un conjunto sofisticado de protocolos de modificación y bloqueos distribuidos; cosas que están más allá del alcance de una utilidad de edición de archivos típica
Entonces, si desea editar un archivo mientras lo procesa un shell, tiene dos opciones:
Puedes adjuntarlo al archivo. Esto siempre debería funcionar.
Puede sobrescribir el archivo con nuevos contenidos de exactamente la misma longitud . Esto puede o no funcionar, dependiendo de si el shell ya ha leído esa parte del archivo o no. Dado que la mayoría de las E / S de archivos implican memorias intermedias de lectura, y dado que todos los shells que conozco leen un comando compuesto completo antes de ejecutarlo, es bastante poco probable que pueda salirse con la suya. Ciertamente no sería confiable.
No conozco ninguna redacción en el estándar Posix que realmente requiera la posibilidad de agregar un archivo de script mientras se ejecuta el archivo, por lo que podría no funcionar con cada shell compatible con Posix, mucho menos con la oferta actual de casi- y, a veces, conchas compatibles con posix. Entonces YMMV. Pero hasta donde yo sé, funciona de manera confiable con bash.
Como evidencia, aquí hay una implementación "sin bucles" del infame programa de 99 botellas de cerveza en bash, que se utiliza
dd
para sobrescribir y agregar (la sobrescritura es presumiblemente segura porque sustituye a la línea que se está ejecutando actualmente, que siempre es la última línea del archivo, con un comentario de exactamente la misma longitud; lo hice para que el resultado final pueda ejecutarse sin el comportamiento de modificación automática).fuente
export beer=100
antes de ejecutar el script, funciona como se esperaba.bash
hace un largo camino para asegurarse de que lee los comandos justo antes de ejecutarlos.Por ejemplo en:
El shell leerá el script por bloques, por lo que es probable que lea ambos comandos, interprete el primero y luego busque el final del
cmd1
script y lea el script nuevamente para leerlocmd2
y ejecutarlo.Puedes verificarlo fácilmente:
(aunque mirando el
strace
resultado en eso, parece que hace algunas cosas más elegantes (como leer los datos varias veces, buscar de nuevo ...) que cuando intenté lo mismo hace unos años, por lo que mi declaración anterior sobre buscar de nuevo puede ya no se aplica en versiones más recientes).Sin embargo, si escribe su script como:
El shell tendrá que leer hasta el cierre
}
, almacenarlo en la memoria y ejecutarlo. Debido a estoexit
, el shell no volverá a leer el script para que pueda editarlo de manera segura mientras el shell lo está interpretando.Alternativamente, cuando edite el guión, asegúrese de escribir una nueva copia del guión. El shell seguirá leyendo el original (incluso si se elimina o cambia de nombre).
Para hacer eso, cambiar el nombre
the-script
dethe-script.old
y copiarthe-script.old
athe-script
y editarlo.fuente
Realmente no hay una forma segura de modificar el script mientras se está ejecutando porque el shell puede usar el almacenamiento en búfer para leer el archivo. Además, si el script se modifica al reemplazarlo con un nuevo archivo, los shells normalmente solo leerán el nuevo archivo después de realizar ciertas operaciones.
A menudo, cuando se cambia un script mientras se ejecuta, el shell termina informando errores de sintaxis. Esto se debe al hecho de que, cuando el shell cierra y vuelve a abrir el archivo de secuencia de comandos, utiliza el desplazamiento de bytes en el archivo para reposicionarse a su regreso.
fuente
Puede evitar esto estableciendo una trampa en su secuencia de comandos y luego utilizando
exec
para recoger el nuevo contenido de la secuencia de comandos. Sin embargo,exec
tenga en cuenta que la llamada inicia el script desde cero y no desde donde llegó en el proceso de ejecución, por lo que se llamará al script B (en adelante).Esto continuará mostrando la fecha en la pantalla. Luego podría editar mi script y cambiar
date
aecho "Date: $(date)"
. Al escribir eso, el script en ejecución solo muestra la fecha. Sin embargo, si envío la señal que configurétrap
para capturar, el scriptexec
(reemplaza el proceso actual en ejecución con el comando especificado), que es el comando$CMD
y los argumentos$@
. Puede hacerlo emitiendokill -1 PID
, donde PID es el PID del script en ejecución, y la salida cambia para mostrarseDate:
antes de ladate
salida del comando.Puede almacenar el "estado" de su secuencia de comandos en un archivo externo (en say / tmp) y leer el contenido para saber dónde "reanudar" cuando el programa se vuelve a ejecutar. A continuación, puede agregar una terminación de trampas adicional (SIGINT / SIGQUIT / SIGKILL / SIGTERM) para borrar ese archivo tmp para que cuando reinicie después de interrumpir el "Script A", comience desde el principio. Una versión con estado sería algo como:
fuente
$0
y$@
al comienzo del script y usando esas variables en suexec
lugar.