Por lo general, si edita un scrpit, todos los usos en ejecución del script son propensos a errores.
Por lo que yo entiendo, bash (¿otros shells también?) Lee el script de forma incremental, por lo que si modifica el archivo de script externamente, comienza a leer las cosas incorrectas. Hay alguna manera de prevenirlo?
Ejemplo:
sleep 20
echo test
Si ejecuta este script, bash leerá la primera línea (digamos 10 bytes) y se irá a dormir. Cuando se reanuda, puede haber diferentes contenidos en la secuencia de comandos a partir del décimo byte. Puedo estar en el medio de una línea en el nuevo guión. Por lo tanto, el script en ejecución se romperá.
\n
haría el truco? Tal vez una subshell()
servirá? No tengo mucha experiencia, ¡por favor ayuda!sleep 20 ;\n echo test ;\n sleep 20
y empiezo a editarlo, puede comportarse mal. Por ejemplo, bash podría leer los primeros 10 bytes del script, comprender elsleep
comando y dormir. Después de que se reanude, habrá diferentes contenidos en el archivo a partir de 10 bytes.Respuestas:
Sí, los shells, y
bash
en particular, tienen cuidado de leer el archivo de una línea a la vez, por lo que funciona igual que cuando lo usa de forma interactiva.Notará que cuando el archivo no se puede buscar (como una tubería),
bash
incluso lee un byte a la vez para asegurarse de no leer más allá del\n
carácter. Cuando se puede buscar el archivo, se optimiza leyendo bloques completos a la vez, pero se vuelve a buscar después de\n
.Eso significa que puedes hacer cosas como:
O escriba guiones que se actualicen a sí mismos. Lo que no podría hacer si no le brindara esa garantía.
Ahora, es raro que desee hacer cosas así y, como descubrió, esa característica tiende a interferir con más frecuencia de lo que es útil.
Para evitarlo, puede intentar asegurarse de no modificar el archivo en el lugar (por ejemplo, modificar una copia y mover la copia en su lugar (como
sed -i
operl -pi
y algunos editores lo hacen, por ejemplo)).O podrías escribir tu guión como:
(tenga en cuenta que es importante que
exit
esté en la misma línea que}
; aunque también podría ponerlo dentro de los corchetes justo antes del cierre).o:
El shell necesitará leer el script hasta
exit
antes de comenzar a hacer algo. Eso asegura que el shell no volverá a leer el script.Eso significa que todo el script se almacenará en la memoria.
Eso también puede afectar el análisis del script.
Por ejemplo, en
bash
:Daría salida a ese U + 00E9 codificado en UTF-8. Sin embargo, si lo cambia a:
El
\ue9
se expandirá en el juego de caracteres que estaba vigente en el momento en que se analizó el comando, que en este caso es antes de queexport
se ejecute el comando.También tenga en cuenta que si se usa el comando
source
aka.
, con algunos shells, tendrá el mismo tipo de problema para los archivos de origen.Sin
bash
embargo, ese no es el caso de cuyosource
comando lee el archivo completamente antes de interpretarlo. Si escribebash
específicamente, podría hacer uso de eso, agregando al comienzo del script:(Sin embargo, no me basaría en eso, ya que podría imaginar que las versiones futuras de
bash
podrían cambiar ese comportamiento que actualmente puede verse como una limitación (bash y AT&T ksh son los únicos shells similares a POSIX que se comportan así hasta donde se sabe) y elalready_sourced
truco es un poco frágil, ya que supone que la variable no está en el entorno, sin mencionar que afecta el contenido de la variable BASH_SOURCE)fuente
}; exec true
. De esta manera, no hay requisitos para las nuevas líneas al final del archivo, lo que es amigable para algunos editores (como emacs). Todas las pruebas en las que podría pensar funcionan correctamente}; exec true
}; exit
? También estás perdiendo el estado de salida.. script
se use el comando dot ( ).Simplemente necesita eliminar el archivo (es decir, copiarlo, eliminarlo, cambiar el nombre de la copia al nombre original). De hecho, muchos editores pueden configurarse para hacer esto por usted. Cuando edite un archivo y guarde un búfer modificado, en lugar de sobrescribir el archivo, cambiará el nombre del archivo antiguo, creará uno nuevo y colocará los nuevos contenidos en el nuevo archivo. Por lo tanto, cualquier script en ejecución debe continuar sin problemas.
Al usar un sistema de control de versiones simple como RCS que está disponible para vim y emacs, obtienes la doble ventaja de tener un historial de tus cambios, y el sistema de pago debería eliminar por defecto el archivo actual y recrearlo con los modos correctos. (Tenga cuidado con la vinculación de dichos archivos, por supuesto).
fuente
La solución más simple:
De esta manera, bash leerá todo el
{}
bloque antes de ejecutarlo, y elexit
directiva se asegurará de que no se lea nada fuera del bloque de código.Si no desea "ejecutar" el script, sino más bien "fuente", necesita una solución diferente. Esto debería funcionar entonces:
O si desea control directo sobre el código de salida:
Voilà! Este script es seguro para editar, obtener y ejecutar. Aún debe asegurarse de no modificarlo en esos milisegundos cuando se está leyendo inicialmente.
fuente
Prueba de concepto. Aquí hay un script que se modifica a sí mismo:
vemos la versión modificada imprimir
Esto se debe a que bash cargas mantiene un identificador de archivo para abrir en el script, por lo que los cambios en el archivo se verán de inmediato.
Si no desea actualizar la copia en memoria, desvincula el archivo original y reemplácelo.
Una forma de hacerlo es usando sed -i.
prueba de concepto
Si está utilizando un editor para cambiar la secuencia de comandos, habilitar la función "mantener una copia de seguridad" puede ser todo lo que se necesita para que el editor escriba la versión modificada en un nuevo archivo en lugar de sobrescribir la existente.
fuente
bash
no abre el archivo conmmap()
. Es cuidadoso leer una línea a la vez según sea necesario, como cuando recibe los comandos de un dispositivo terminal cuando es interactivo.Envolver su script en un bloque
{}
es probablemente la mejor opción, pero requiere cambiar sus scripts.sería la segunda mejor opción (suponiendo tmpfs ) la desventaja es que rompe $ 0 si sus scripts usan eso.
usar algo así
F=test.sh; tail -n $(cat "$F" | wc -l) "$F" | bash
es menos ideal porque tiene que mantener todo el archivo en la memoria y rompe $ 0.se debe evitar tocar el archivo original para que la última hora de modificación, los bloqueos de lectura y los enlaces duros no se vean alterados. de esa manera puede dejar un editor abierto mientras ejecuta el archivo y rsync no hará innecesariamente la suma de comprobación del archivo para que las copias de seguridad y los enlaces duros funcionen como se esperaba.
reemplazar el archivo en la edición funcionaría, pero es menos robusto porque no es exigible a otros scripts / usuarios / o uno podría olvidarlo. Y nuevamente rompería los enlaces duros.
fuente
tac test.sh | tac | bash