Estaba escribiendo un script bash, y se me ocurrió actualizar el código (guardé el archivo del script en el disco) mientras el script esperaba alguna entrada en un while
bucle. Después de regresar al terminal y continuar con la invocación previa del script, bash dio un error sobre la sintaxis del archivo:
/home/aularon/bin/script: line 58: unexpected EOF while looking for matching `"'
/home/aularon/bin/script: line 67: syntax error: unexpected end of file
Entonces intenté hacer lo siguiente:
Primero: crea un script, self-update.sh
llamémoslo:
#!/bin/bash
fname=$(mktemp)
cat $0 | sed 's/BEFORE\./AFTER!./' > $fname
cp $fname $0
rm -f $fname
echo 'String: BEFORE.';
Lo que hace el script es leer su código, cambiar la palabra 'ANTES' a 'DESPUÉS', luego reescribirse con el nuevo código.
2º Ejecutarlo:
chmod +x self-update.sh
./self-update.sh
Tercera maravilla ...
aularon@aularon-laptop:~$ ./self-update.sh
String: AFTER!.
¡Ahora, no habría adivinado que en la misma invocación saldría DESPUÉS! , en la segunda carrera seguro, pero no en la primera.
Entonces mi pregunta es: ¿es intencional (por diseño)? o es por la forma en que bash ejecuta el script? Línea por línea o comando por comando. ¿Hay algún buen uso de tal comportamiento? ¿Algún ejemplo de ello?
Editar: intenté formatear el archivo para poner todos los comandos en una línea, no funciona ahora:
#!/bin/bash
fname=$(mktemp);cat $0 | sed 's/BEFORE\./AFTER!./' > $fname;cp $fname $0;rm -f $fname;echo 'String: BEFORE.';
Salida:
aularon@aularon-laptop:~$ ./self-update.sh #First invocation
String: BEFORE.
aularon@aularon-laptop:~$ ./self-update.sh #Second invocation
String: AFTER!.
Mientras mueve la echo
cadena a la siguiente línea, separándola de la cp
llamada rewriting ( ):
#!/bin/bash
fname=$(mktemp);cat $0 | sed 's/BEFORE\./AFTER!./' > $fname;cp $fname $0;rm -f $fname;
echo 'String: BEFORE.';
Y ahora funciona de nuevo:
aularon@aularon-laptop:~$ ./self-update.sh
String: AFTER!.
Respuestas:
Esto es por diseño. Bash lee guiones en trozos. Entonces leerá una parte del script, ejecutará las líneas que pueda y luego leerá el siguiente fragmento.
Entonces te encuentras con algo como esto:
Donde esto se vuelve aún más problemático es que si editas algo antes del byte 256. Digamos que eliminas un par de líneas. Entonces, los datos en el script que estaban en el byte 256, ahora están en otro lugar, digamos en el byte 156 (100 bytes antes). Debido a esto, cuando bash continúe leyendo, obtendrá lo que originalmente era 356.
Esto es solo un ejemplo. Bash no necesariamente lee 256 bytes a la vez. No sé exactamente cuánto lee a la vez, pero no importa, el comportamiento sigue siendo el mismo.
fuente
stat
el archivo para ver si ha cambiado. No haylseek
llamadasecho foo
cambió a aecho bar
durante elsleep
. Se ha comportado así desde las versiones 2, así que no creo que sea un problema de versión.