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
o perl
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).
#!/bin/bash
if [[ $1 == reset ]]; then
printf "%s\n%-16s#\n" '####' 'next ${1:-99}' |
dd if=/dev/stdin of=$0 seek=$(grep -bom1 ^#### $0 | cut -f1 -d:) bs=1 2>/dev/null
exit
fi
step() {
s=s
one=one
case $beer in
2) beer=1; unset s;;
1) beer="No more"; one=it;;
"No more") beer=99; return 1;;
*) ((--beer));;
esac
}
next() {
step ${beer:=$(($1+1))}
refrain |
dd if=/dev/stdin of=$0 seek=$(grep -bom1 ^next\ $0 | cut -f1 -d:) bs=1 conv=notrunc 2>/dev/null
}
refrain() {
printf "%-17s\n" "# $beer bottles"
echo echo ${beer:-No more} bottle$s of beer on the wall, ${beer:-No more} bottle$s of beer.
if step; then
echo echo Take $one down, pass it around, $beer bottle$s of beer on the wall.
echo echo
echo next abcdefghijkl
else
echo echo Go to the store, buy some more, $beer bottle$s of beer on the wall.
fi
}
####
next ${1:-99} #