¿Cómo crear un archivo temporal en el script de shell?

155

Mientras ejecuto un script, quiero crear un archivo temporal en el /tmpdirectorio.

Después de la ejecución de ese script, ese script lo limpiará.

¿Cómo hacer eso en el script de shell?

Bhuvanesh
fuente

Respuestas:

198
tmpfile=$(mktemp /tmp/abc-script.XXXXXX)
: ...
rm "$tmpfile"

Puede asegurarse de que se elimine un archivo cuando salga la secuencia de comandos (incluidas las muertes y bloqueos) abriendo un descriptor de archivo en el archivo y eliminándolo. El archivo se mantiene disponible (para el script; en realidad no para otros procesos, pero /proc/$PID/fd/$FDes una solución alternativa) siempre que el descriptor de archivo esté abierto. Cuando se cierra (lo que el núcleo hace automáticamente cuando sale el proceso) el sistema de archivos elimina el archivo.

tmpfile=$(mktemp /tmp/abc-script.XXXXXX)
exec 3>"$tmpfile"
rm "$tmpfile"
: ...
echo foo >&3
Hauke ​​Laging
fuente
44
Buena respuesta, solución elegante con el descriptor de archivos en caso de bloqueo +1
caos
2
/proc- excepto para sistemas que no lo tienen.
Dennis Williamson
44
lo que hace el exec 3> "$tmpfile"hacer? ¿No es eso útil solo si tmpfile es un script independiente?
Alexej Magura
55
¿Cómo se lee desde el FD creado?
eckes
3
"Puedes usar cat <3 o algo similar". en realidad eso se lee de un archivo llamado 3 @ dragon788. Además, cat <&3dará Bad file descriptor. Le agradecería si lo arregla o lo elimina; la desinformación no ayuda mucho.
Daniel Farrell
65

Use mktemppara crear un archivo o directorio temporal:

temp_file=$(mktemp)

O para un direcotry:

temp_dir=$(mktemp -d)

Al final del script, debe eliminar el archivo / directorio temporal:

rm ${temp_file}
rm -R ${temp_dir}

mktemp crea un archivo en el /tmpdirectorio o en el directorio dado con el --tmpdirargumento.

caos
fuente
20
Puede usarlo trap "rm -f $temp_file" 0 2 3 15inmediatamente después de crear el archivo para que cuando el script salga o se detenga con ctrl-Cel archivo, aún se elimine.
wurtel
1
@wurtel ¿Qué pasa si EXITes el único gancho para trap?
Hauke ​​Laging
44
@HaukeLaging Entonces la trampa no se dispara si el script se detiene con Ctrl + C. Una cosa a tener en cuenta es que TRAP no ayuda si usted kill -9 $somepid. Esa señal particular de muerte es muerte instantánea sin que nada más suceda.
dragon788
55
@ dragon788 ¿Has probado eso? Debieras. bash -c 'echo $$; trap "echo foo" 0; sleep 5'
Hauke ​​Laging
Atrapar EXITes suficiente.
Kusalananda
15

Si está en un sistema que tiene mktemp , debe usarlo como otras respuestas.

Con POSIX toolchest:

umask 0177
tmpfile=/tmp/"$0"."$$"."$(awk 'BEGIN {srand();printf "%d\n", rand() * 10^10}')"
trap 'rm -f -- "$tmpfile"' INT TERM HUP EXIT
: > "$tmpfile"
Cuonglm
fuente
¿Qué pasa si EXITes el único gancho para trap?
Hauke ​​Laging
@HaukeLaging: tmpfileaún se debe eliminar antes de la salida del script, pero no cuando el script recibió otras señales.
Cuonglm
Eso no es lo que sucede aquí (GNU bash, Versión 4.2.53).
Hauke ​​Laging
@HaukeLaging: ¿Qué quieres decir That's not what happens?
Cuonglm
3
mktempse originó en HP / UX con una sintaxis diferente. Todd C. Miller creó uno diferente para OpenBSD a mediados de los 90 (copiado por FreeBSD y NetBSD) y más tarde también lo hizo disponible como una utilidad independiente (www.mktemp.org). Ese es el que generalmente se usaba en Linux hasta que mktempse agregó una utilidad (en su mayoría compatible) a los coreutils de GNU en 2007. Solo para decir que uno realmente no puede decir mktempes una utilidad de GNU.
Stéphane Chazelas
14

Algunos proyectiles tienen la característica incorporada.

zsh

zshLa =(...)forma de sustitución del proceso utiliza un archivo temporal. Por ejemplo, se =(echo test)expande a la ruta de un archivo temporal que contiene test\n.

$ {cat $file; ls -l /dev/fd/3; echo test2 >&3; cat $file} 3<> ${file::==(echo test)}
test
lrwx------ 1 stephane stephane 64 Jan 30 11:19 /dev/fd/3 -> /tmp/zshMLbER0
test2

Ese archivo se elimina automáticamente, una vez que el comando ha finalizado.

bash / zsh en Linux.

Here-files o here-strings in bashy zshse implementan como archivos temporales eliminados.

Entonces si lo haces:

exec 3<<< test

El descriptor de archivo 3 está conectado a un archivo temporal eliminado que contiene test\n.

Puede obtener su contenido con:

cat <&3

Si está en Linux, también puede leer o escribir en ese archivo a través de /dev/fd/3

$ exec 3<<< test
$ cat <&3
test
$ echo foo > /dev/fd/3
$ cat /dev/fd/3
foo

(algunos otros depósitos usan tuberías, o pueden usarse /dev/nullsi el documento here está vacío).

POSIX

No hay mktemputilidad POSIX. POSIX, sin embargo, especifica una mkstemp(template)API de C , y la m4utilidad estándar expone esa API con la mkstemp()función m4 con el mismo nombre.

mkstemp()le da un nombre de archivo con una parte aleatoria que se garantizó que no existía en el momento en que se llamó a la función. Sí crea el archivo con permisos 0600 de forma libre de carrera.

Entonces, podrías hacer:

tmpfile=$(
  echo 'mkstemp(template)' |
    m4 -D template="${TMPDIR:-/tmp}/baseXXXXXX"
) || exit

Sin embargo, tenga en cuenta que debe manejar la limpieza al salir, aunque si solo necesita escribir y leer el archivo un número fijo de veces, puede abrirlo y eliminarlo justo después de crearlo para here-doc / here- enfoque de cuerda arriba:

tmpfile=$(
  echo 'mkstemp(template)' |
    m4 -D template="${TMPDIR:-/tmp}/baseXXXXXX"
) || exit

# open once for writing, twice for reading:
exec 3> "$tempfile" 4< "$tempfile" 5< "$tempfile"

rm -f -- "$tmpfile"

cmd >&3   # store something in the temp file
exec 3>&- # fd no longer needed

# read the content twice:
cat <&4
cat <&5

Puede abrir el archivo para leerlo una vez y rebobinar entre dos lecturas, sin embargo, no hay una utilidad POSIX que pueda rebobinar ( lseek()), por lo que no puede hacerlo de forma portátil en un script POSIX ( zsh( sysseekincorporado) y ksh93( <#((...))operador) puede hazlo sin embargo).

Stéphane Chazelas
fuente
1
Bash también tiene sustitución de procesos usando<()
WinnieNicklaus
3
@ WinnieNicklaus, sí, pero eso no usa archivos temporales, por lo que es irrelevante aquí. Sustitución proceso fue introducido por ksh, copiado por bash y zsh, y zsh extendió con una forma tercero: =(...).
Stéphane Chazelas
7

Aquí hay una respuesta un poco mejorada en la línea de Hauke ​​Laging:

#!/bin/bash

tmpfile=$(mktemp)  # Create a temporal file in the default temporal folder of the system

# Lets do some magic for the tmpfile to be removed when this script ends, even if it crashes
exec {FD_W}>"$tmpfile"  # Create file descriptor for writing, using first number available
exec {FD_R}<"$tmpfile"  # Create file descriptor for reading, using first number available
rm "$tmpfile"  # Delete the file, but file descriptors keep available for this script

# Now it is possible to work with the temporal file
echo foo >&$FD_W
echo bar >&$FD_W  # Note that file descriptor always concatenates, not overwrites

cat <&$FD_R
Cisnes
fuente
2
Cabe señalar que el contenido está disponible solo una vez. Es decir, si hago cat <& $ FD_R por segunda vez, no se produce ninguna salida. Ver unix.stackexchange.com/questions/166482/… . ¿Hay alguna forma de eliminar automáticamente el archivo si el programa falla, pero haciéndolo accesible varias veces?
smihael
0

Mi flujo de trabajo normalmente con archivos temporales se debe a algún script bash que estoy probando. Quiero teehacerlo para poder ver que está funcionando y guardar la salida para la próxima iteración de mi proceso. He creado un archivo llamadotmp

#!/bin/bash
echo $(mktemp /tmp/$(date +"%Y-%m-%d_%T_XXXXXX"))

para que pueda usarlo como

$ some_command --with --lots --of --stuff | tee $(tmp)

La razón por la que me gusta la fecha y hora formateada antes de los valores aleatorios es que me permite encontrar el archivo tmp que acabo de crear fácilmente, y no tengo que pensar en cómo nombrarlo la próxima vez (y centrarme en obtener mi script dang) trabajar).

Frank Bryce
fuente