2010-12-22 13 views
21

Tengo un programa bash que escribirá en un archivo de salida. Este archivo puede o no existir, pero el script debe verificar los permisos y fallar anticipadamente. No puedo encontrar una manera elegante de hacer que esto suceda. Esto es lo que he intentado.Bash: cree un archivo si no existe, de lo contrario verifique si es grabable

 
set +e 
touch $file 
set -e 

if [ $? -ne 0 ]; then exit;fi 

guardo en set -e para este script por lo que falla si alguna vez hay un error en cualquier línea. ¿Hay alguna manera más fácil de hacer el script anterior?

+1

Debo advertirle que hay una condición de carrera. No puede verificar un error temprano. El archivo podría haber estado allí en el momento en que lo verificó, pero se eliminó cuando está a punto de comenzar a escribir en él. O peor aún, el permiso cambió. Te recomiendo que abras el archivo para escribir y obtener el descriptor. Si esa operación tuvo éxito, estás bien.En ese caso, no importa si el archivo se eliminó o no, OS mantendrá abierto el inodo en caso de eliminación y simplemente lo "tocará" desde el nombre del archivo. –

+0

Ayúdame a comprender. Si el archivo de registro se elimina mientras el script se está ejecutando, ¿los intentos de escribir en él tendrán éxito si abro un descriptor? Pero el archivo se borra ... ¿a dónde van los datos? ¿No quisiera que fallara en esos raros casos? – User1

+1

Si se elimina un archivo (más específicamente, no vinculado) mientras está abierto para escritura, permanece en el sistema de archivos hasta que se cierran todos los manejadores de archivos, ya sea explícitamente o por el proceso (s) que sale. Si su proceso es lo único que se escribe en el archivo, continuará llenando el disco, pero no se podrá abrir (porque no hay entrada de directorio) por ningún otro proceso. – Phil

Respuesta

20

En lugar de comprobar $? en una línea diferente, comprobar el valor de retorno inmediato de esta manera:

touch file || exit 

Mientras su umask no restringe el bit de escritura de estar ajustado, que sólo puede confíe en el valor de retorno de touch

+2

Tenga en cuenta que no tiene que 'establecer + e' con esto, ya que' -e' solo afecta a comandos simples. Además, si desea imprimir un mensaje de error, puede usar 'touch file || {echo "No se puede escribir en el archivo"> ​​& 2; exit 1} ' –

+0

@Gordon Buena llamada en' -e'. Además, originalmente recibí el mensaje de error, pero luego me di cuenta de que 'touch' ya hace un gran trabajo:' touch: no se puede tocar '/ root/blah': Permiso denegado' – SiegeX

+0

Esto no es robusto: garantiza que el archivo escrito * cuando ejecuta 'touch' *. Es posible que no se pueda escribir más tarde cuando lo necesite. Dependiendo de la situación, esto podría causar condiciones de carrera o agujeros de seguridad. – Gilles

2

Puede usar -w para verificar si un archivo puede escribirse (búsquelo en la página man de bash).

if [[ ! -w $file ]]; then exit; fi 
+1

Eso tendría que hacerse después del comando táctil ya que el archivo podría no existir de antemano. –

2

Abra el archivo para escribir. En el shell, esto se hace con una redirección de salida. Puede redirigir la salida estándar del shell colocando la redirección en el exec integrado sin ningún argumento.

set -e 
exec >shell.out # exit if shell.out can't be opened 
echo "This will appear in shell.out" 

Asegúrese de no haber configurado la opción noclobber (que es útil de forma interactiva, pero a menudo inutilizable en escrituras). Use > si desea truncar el archivo si existe, y >> si desea agregarlo.

Si solo desea probar los permisos, puede ejecutar : >foo.out para crear el archivo (o truncarlo si existe).

Si solo desea que algunos comandos escriban en el archivo, ábralo en otro descriptor, luego redirija según sea necesario.

set -e 
exec 3>foo.out 
echo "This will appear on the standard output" 
echo >&3 "This will appear in foo.out" 
echo "This will appear both on standard output and in foo.out" | tee /dev/fd/3 

(/dev/fd no se admite en todas partes, está disponible al menos en Linux, * BSD, Solaris y Cygwin.)

30

Por qué complicar las cosas?

file=exists_and_writeable 

if [ ! -e "$file" ] ; then 
    touch "$file" 
fi 

if [ ! -w "$file" ] ; then 
    echo cannot write to $file 
    exit 1 
fi 

O, más concisa,

([ -e "$file" ] || touch "$file") && [ ! -w "$file" ] && echo cannot write to $file && exit 1 
3

¿Por qué debe el guión fallar antes de tiempo? Al separar la prueba de escritura y el archivo abrir() introduce una condición de carrera. En su lugar, ¿por qué no intentas abrir (truncar/anexar) el archivo para escribir y lidiar con el error si ocurre? Algo así como:

$ echo foo > output.txt 
$ if [ $? -ne 0 ]; then die("Couldn't echo foo") 

Como otros mencionar, la opción "noclobber" podría ser útil si se quiere evitar sobrescribir los archivos existentes.

Cuestiones relacionadas