Crear archivos temporales en bash

150

¿Hay formas objetivamente mejores de crear archivos temporales en scripts de bash?

Normalmente solo los nombro lo que se me ocurre, como tempfile-123, ya que se eliminará cuando termine el script. ¿Hay alguna desventaja en hacer esto además de sobrescribir un posible archivo temporal 123 en la carpeta actual? ¿O hay alguna ventaja en crear un archivo temporal de una manera más cuidadosa?

Strapakowsky
fuente
1
No uses archivos temporales. Use directorios temporales en su lugar. Y no uses mktemp. Vea aquí por qué: codeproject.com/Articles/15956/…
ceving
17
@ceving Ese artículo es simplemente incorrecto, al menos cuando se aplica al comando de shell mktemp (a diferencia de la llamada a la biblioteca mktemp). Como mktemp crea el archivo con una máscara u restrictiva, el ataque dado solo funciona si el atacante está operando bajo la misma cuenta que el atacado ... en cuyo caso el juego ya está perdido. Para conocer las mejores prácticas en el mundo de las secuencias de comandos de shell, consulte mywiki.wooledge.org/BashFAQ/062
Charles Duffy
También puede usarlo tempfile(1)en sistemas que lo tengan.
Pausado hasta nuevo aviso.

Respuestas:

179

La mktemp(1)página del manual lo explica bastante bien:

Tradicionalmente, muchos scripts de shell toman el nombre del programa con el pid como sufijo y lo usan como un nombre de archivo temporal. Este tipo de esquema de nombres es predecible y la condición de carrera que crea es fácil de ganar para un atacante. Un enfoque más seguro, aunque aún inferior, es crear un directorio temporal utilizando el mismo esquema de nombres. Si bien esto permite garantizar que un archivo temporal no se subvertirá, todavía permite un simple ataque de denegación de servicio. Por estas razones, se sugiere utilizar mktemp en su lugar.

En un script, invoco mktemp algo como

mydir=$(mktemp -d "${TMPDIR:-/tmp/}$(basename $0).XXXXXXXXXXXX")

que crea un directorio temporal en el que puedo trabajar y en el que puedo nombrar de forma segura los archivos reales como algo legible y útil.

mktempno es estándar, pero existe en muchas plataformas. Las "X" generalmente se convertirán en algo aleatorio, y más probablemente sea más aleatorio; sin embargo, algunos sistemas (busybox ash, por ejemplo) limitan esta aleatoriedad más significativamente que otros


Por cierto, la creación segura de archivos temporales es importante para algo más que solo secuencias de comandos de shell. Por eso Python tiene tempfile , Perl tiene File :: Temperatura , rubí tiene de archivo temporal , etc ...

kojiro
fuente
1
La opción -t está en desuso: gnu.org/software/coreutils/manual/html_node/…
Tibor Vass
77
Parece que la forma más segura y más multiplataforma de usar mktempes en combinación con basename, como así mktemp -dt "$(basename $0). XXXXXXXXXX". Si se usa sin basenameusted, puede obtener un error como este mktemp: plantilla no válida, `/tmp/MOB-SAN-JOB1-183-ScriptBuildTask-7300464891856663368.sh.XXXXXXXXXX ', contiene un separador de directorio .
i4niac
77
Ignora el error tipográfico (espacio extra). mktemp -dt "$(basename $0).XXXXXXXXXX"Es la forma correcta.
i4niac
3
@ i4niac: debe citar eso $0, hay muchos espacios en la tierra de OS X.mktemp -dt "$(basename "$0").XXXXXX"
Orwellophile
11
También puede ser bueno eliminar el tempdir al final de la ejecución del script:trap "rm -rf $mydir" EXIT
KumZ
43

Sí, usa mktemp .

Creará un archivo temporal dentro de una carpeta diseñada para almacenar archivos temporales, y le garantizará un nombre único. Produce el nombre de ese archivo:

> mktemp
/tmp/tmp.xx4mM3ePQY
>
Pablo
fuente
19

Es posible que desee mirar mktemp

La utilidad mktemp toma la plantilla de nombre de archivo dada y sobrescribe una parte de ella para crear un nombre de archivo único. La plantilla puede ser cualquier nombre de archivo con algún número de 'X' adjuntas, por ejemplo /tmp/tfile.XXXXXXXXXX. Las 'X' finales se reemplazan con una combinación del número de proceso actual y letras aleatorias.

Para más detalles: man mktemp

John Lawrence
fuente
11

¿Hay alguna ventaja en crear un archivo temporal de una manera más cuidadosa

Los archivos temporales generalmente se crean en el directorio temporal (como /tmp) donde todos los demás usuarios y procesos tienen acceso de lectura y escritura (cualquier otro script puede crear los nuevos archivos allí). Por lo tanto, el script debe tener cuidado al crear los archivos, como usarlos con los permisos correctos (por ejemplo, leer solo para el propietario, ver help umask:) y el nombre de archivo no debe adivinarse fácilmente (idealmente al azar). De lo contrario, si los nombres de archivo no son únicos, puede crear conflictos con el mismo script ejecutado varias veces (por ejemplo, condición de carrera) o algún atacante podría secuestrar alguna información confidencial (por ejemplo, cuando los permisos son demasiado abiertos y el nombre de archivo es fácil de adivinar) o crear / reemplazar el archivo con su propia versión del código (como reemplazar los comandos o consultas SQL dependiendo de lo que se esté utilizando) almacenado).


Puede usar el siguiente enfoque para crear el directorio temporal:

TMPDIR=".${0##*/}-$$" && mkdir -v "$TMPDIR"

o archivo temporal:

TMPFILE=".${0##*/}-$$" && touch "$TMPFILE"

Sin embargo, todavía es predecible y no se considera seguro.

Según man mktemp, podemos leer:

Tradicionalmente, muchos scripts de shell toman el nombre del programa con el pid como sufijo y lo usan como un nombre de archivo temporal. Este tipo de esquema de nombres es predecible y la condición de carrera que crea es fácil de ganar para un atacante.

Por lo tanto, para estar seguro, se recomienda usar el mktempcomando para crear un archivo o directorio temporal único ( -d).

kenorb
fuente
2
No es exactamente lo que se le preguntó. Sin embargo, puede ser una solución perfecta.
jpbochi
1
@jpbochi He mejorado la respuesta para abordar la pregunta. Avísame si eso ayuda.
kenorb
2
Mejora la respuesta de hecho. Sin embargo, mi voto a favor ya era tuyo. No puedo votar más. Una sugerencia que tengo es explicar qué ${0##*/}y $$ampliar, o vincular a alguna documentación al respecto.
jpbochi
0

mktemp es probablemente el más versátil, especialmente si planea trabajar con el archivo por un tiempo.

También puede usar un operador de sustitución de procesos <() si solo necesita el archivo temporalmente como entrada para otro comando, por ejemplo:

$ diff <(echo hello world) <(echo foo bar)
Barry Jones
fuente