¿Podemos usar carpetas temporales como archivos temporales?
TMP=$(mktemp ... )
exec 3<>$TMP
rm $TMP
cat <&3
que se destruirá automáticamente después de esta salida de shell?
¿Podemos usar carpetas temporales como archivos temporales?
TMP=$(mktemp ... )
exec 3<>$TMP
rm $TMP
cat <&3
que se destruirá automáticamente después de esta salida de shell?
Respuestas:
En el caso de un archivo temporal, su ejemplo en la pregunta lo crearía, luego lo desvincularía del directorio (haciéndolo "desaparecer"), y cuando el script cierra el descriptor de archivo (probablemente al finalizar), el espacio ocupado por el archivo sería reclamable por el sistema. Esta es una forma común de manejar archivos temporales en idiomas como C.
Por lo que sé, no es posible abrir un directorio de la misma manera, al menos de ninguna manera que haga que el directorio sea utilizable.
Una forma común de eliminar archivos y directorios temporales al finalizar un script es mediante la instalación de una EXIT
trampa de limpieza . Los ejemplos de código que se proporcionan a continuación evitan tener que hacer malabarismos con los descriptores de archivos por completo.
tmpdir=$(mktemp -d)
tmpfile=$(mktemp)
trap 'rm -f "$tmpfile"; rm -rf "$tmpdir"' EXIT
# The rest of the script goes here.
O puede llamar a una función de limpieza:
cleanup () {
rm -f "$tmpfile"
rm -rf "$tmpdir"
}
tmpdir=$(mktemp -d)
tmpfile=$(mktemp)
trap cleanup EXIT
# The rest of the script goes here.
La EXIT
trampa no se ejecutará al recibir la KILL
señal (que no puede ser atrapada), lo que significa que no se realizará ninguna limpieza en ese momento. Sin embargo, se ejecutará cuando finalice debido a una señal INT
o TERM
(si se ejecuta con bash
o ksh
, en otros shells, es posible que desee agregar estas señales después EXIT
en la trap
línea de comando), o cuando salga normalmente debido a que llega al final del script o ejecuta un exit
llamada.
.
y ..
. (Probado en Linux, no sé si eso es coherente en todas las plataformas)
exec another-command
obviamente.
Escriba una función de shell que se ejecutará cuando finalice su script. En el siguiente ejemplo, lo llamo 'limpieza' y configuro una trampa para que se ejecute en los niveles de salida, como: 0 1 2 3 6
trap cleanup 0 1 2 3 6
cleanup()
{
[ -d $TMP ] && rm -rf $TMP
}
Vea esta publicación para más información.
cleanup
antes de una salida limpia (0) y al recibir SIGHUP (1), SIGINT (2), SIGQUIT (3) y SIGABRT (6). será no correr cleanup
cuando las salidas de guión debido a SIGTERM, SIGSEGV, SIGKILL, SIGPIPE, etc. Esto es claramente deficiente.
Puede introducirlo y luego eliminarlo, siempre que no intente utilizar rutas dentro de él después:
#! /bin/sh
dir=`mktemp -d`
cd "$dir"
exec 4>file 3<file
rm -fr "$dir"
echo yes >&4 # OK
cat <&3 # OK
cat file # FAIL
echo yes > file # FAIL
No lo he comprobado, pero lo más probable es que sea el mismo problema cuando utilizo openat (2) en C con un directorio que ya no existe en el sistema de archivos.
Si eres root y tienes Linux, puedes jugar con un espacio de nombres separado y mount -t tmpfs tmpfs /dir
dentro de él.
Las respuestas canónicas (establecer una trampa en EXIT) no funcionan si su script se ve forzado a salir impuro (por ejemplo, con SIGKILL); eso puede dejar datos confidenciales dando vueltas.
Actualizar:
Aquí hay una pequeña utilidad que implementa el enfoque de espacio de nombres. Debería compilarse con
cc -Wall -Os -s chtmp.c -o chtmp
y CAP_SYS_ADMIN
capacidades de archivo dadas (como root) con
setcap CAP_SYS_ADMIN+ep chtmp
Cuando se ejecuta (como un usuario normal) como
./chtmp command args ...
dejará de compartir su espacio de nombres del sistema de archivos, montará un sistema de archivos tmpfs /proc/sysvipc
, chdir y ejecutará command
con los argumentos dados. command
será no heredar las CAP_SYS_ADMIN
capacidades.
Ese sistema de archivos no será accesible desde otro proceso que no se inicie command
, y desaparecerá mágicamente (con todos los archivos que se crearon dentro de él) cuando command
y sus hijos mueran, sin importar cómo suceda eso. Tenga en cuenta que esto es solo compartir el espacio de nombres de montaje: no hay barreras duras entre command
y otros procesos ejecutados por el mismo usuario; que todavía podría colarse dentro de su espacio de nombres ya sea a través de ptrace(2)
, /proc/PID/cwd
o por otros medios.
El secuestro de lo "inútil" /proc/sysvipc
es, por supuesto, una tontería, pero la alternativa hubiera sido el spam /tmp
con directorios vacíos que tendrían que ser eliminados o complicarían enormemente este pequeño programa con tenedores y esperas. Alternativamente, dir
se puede cambiar por ej. /mnt/chtmp
y que sea creado por root en la instalación; no lo haga configurable por el usuario y no lo configure en una ruta de propiedad del usuario, ya que eso puede exponerlo a trampas de enlaces simbólicos y otras cosas peludas que no vale la pena perder el tiempo.
chtmp.c
#define _GNU_SOURCE
#include <err.h>
#include <sched.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/mount.h>
int main(int argc, char **argv){
char *dir = "/proc/sysvipc"; /* LOL */
if(argc < 2 || !argv[1]) errx(1, "usage: %s prog args ...", *argv);
argv++;
if(unshare(CLONE_NEWNS)) err(1, "unshare(CLONE_NEWNS)");
/* "modern" systemd remounts all mount points MS_SHARED
see the NOTES in mount_namespaces(7); YUCK */
if(mount("none", "/", 0, MS_REC|MS_PRIVATE, 0))
err(1, "mount(/, MS_REC|MS_PRIVATE)");
if(mount("tmpfs", dir, "tmpfs", 0, 0)) err(1, "mount(tmpfs, %s)", dir);
if(chdir(dir)) err(1, "chdir %s", dir);
execvp(*argv, argv);
err(1, "execvp %s", *argv);
}
rm $PWD
trabajo, shell todavía está en ese directorio. Pero no se pueden poner archivos nuevos en esta "carpeta". Lo único que puede hacer es leer / escribir con el archivo & 3, y 4. Así que esto sigue siendo "archivo temporal", no "carpeta temporal".
¿Necesitas un shell específico?
Si zsh es una opción, lea zshexpn(1)
:
Si se usa = (...) en lugar de <(...), el archivo pasado como argumento será el nombre de un archivo temporal que contiene el resultado del proceso de la lista. Esto se puede usar en lugar del formulario <para un programa que espera
lseek
(verlseek(2)
) en el archivo de entrada.[...]
Otro problema surge cada vez que el shell rechaza un trabajo con una sustitución que requiere un archivo temporal, incluido el caso en el que aparece
&!
o&|
al final de un comando que contiene una sustitución. En ese caso, el archivo temporal no se limpiará ya que el shell ya no tiene memoria del trabajo. Una solución alternativa es usar una subshell, por ejemplo,(mycmd =(myoutput)) &!
ya que el subshell bifurcado esperará a que termine el comando y luego eliminará el archivo temporal.
Una solución general para garantizar que la sustitución de un proceso perdure durante un período de tiempo adecuado es pasarla como un parámetro a una función de shell anónima (un fragmento de código de shell que se ejecuta inmediatamente con el alcance de la función). Por ejemplo, este código:
() { print File $1: cat $1 } =(print This be the verse)
produce algo parecido a lo siguiente
File /tmp/zsh6nU0kS: This be the verse
Por ejemplo, uso esto en rifle (parte del administrador de archivos del guardabosques) para descifrar un archivo y luego ejecutar rifle en el archivo temporal, que se elimina cuando finaliza el subproceso. (no te olvides de configurar $TERMCMD
)
# ~/.config/ranger/rifle.conf
...
!ext exe, mime octet-stream$, has gpg, flag t = () { rifle -f F "$1" } =(gpg -dq "$1")