Pouvons-nous utiliser des dossiers temporaires comme des fichiers temporaires
TMP=$(mktemp ... )
exec 3<>$TMP
rm $TMP
cat <&3
qui sera détruit automatiquement après cette sortie du shell?
Pouvons-nous utiliser des dossiers temporaires comme des fichiers temporaires
TMP=$(mktemp ... )
exec 3<>$TMP
rm $TMP
cat <&3
qui sera détruit automatiquement après cette sortie du shell?
Réponses:
Dans le cas d'un fichier temporaire, votre exemple dans la question le créerait, puis le dissocierait du répertoire (le faisant "disparaître"), et lorsque le script ferme le descripteur de fichier (probablement à la fin), l'espace occupé par le fichier serait récupérable par le système. Il s'agit d'une façon courante de gérer les fichiers temporaires dans des langues comme C.
Pour autant que je sache, il n'est pas possible d'ouvrir un répertoire de la même manière, du moins en aucune manière qui rendrait le répertoire utilisable.
Une façon courante de supprimer des fichiers et des répertoires temporaires à la fin d'un script est d'installer un EXIT
piège de nettoyage . Les exemples de code donnés ci-dessous évitent d'avoir à jongler complètement avec les descripteurs de fichiers.
tmpdir=$(mktemp -d)
tmpfile=$(mktemp)
trap 'rm -f "$tmpfile"; rm -rf "$tmpdir"' EXIT
# The rest of the script goes here.
Ou vous pouvez appeler une fonction de nettoyage:
cleanup () {
rm -f "$tmpfile"
rm -rf "$tmpdir"
}
tmpdir=$(mktemp -d)
tmpfile=$(mktemp)
trap cleanup EXIT
# The rest of the script goes here.
L' EXIT
interruption ne sera pas exécutée lors de la réception du KILL
signal (qui ne peut pas être intercepté), ce qui signifie qu'aucun nettoyage ne sera alors effectué. Il s'exécutera cependant à la fin en raison d'un INT
ou d' un TERM
signal (s'il fonctionne avecbash
ou ksh
, dans d'autres shells, vous voudrez peut-être ajouter ces signaux après EXIT
dans la trap
ligne de commande), ou lorsque vous quittez normalement en raison de l'arrivée à la fin du script ou de l'exécution d'un exit
appel.
.
et ..
. (Testé sur Linux, je ne sais pas si c'est cohérent sur toutes les plateformes.)
exec another-command
évidemment.
Écrivez une fonction shell qui sera exécutée lorsque votre script sera terminé. Dans l'exemple ci-dessous, je l'appelle «nettoyage» et je définis un piège à exécuter sur les niveaux de sortie, comme: 0 1 2 3 6
trap cleanup 0 1 2 3 6
cleanup()
{
[ -d $TMP ] && rm -rf $TMP
}
Voir cet article pour plus d'informations.
cleanup
avant une sortie propre (0) et à la réception de SIGHUP (1), SIGINT (2), SIGQUIT (3) et SIGABRT (6). il ne s'exécutera pascleanup
lorsque le script se fermera à cause de SIGTERM, SIGSEGV, SIGKILL, SIGPIPE, etc. Ceci est clairement déficient.
Vous pouvez y chdir puis le supprimer, à condition de ne pas essayer d'utiliser des chemins à l'intérieur par la suite:
#! /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
Je n'ai pas vérifié, mais c'est probablement le même problème lorsque vous utilisez openat (2) en C avec un répertoire qui n'existe plus dans le système de fichiers.
Si vous êtes root et sous Linux, vous pouvez jouer avec un espace de noms séparé, et mount -t tmpfs tmpfs /dir
intérieur.
Les réponses canoniques (définir un piège sur EXIT) ne fonctionnent pas si votre script est forcé dans une sortie impure (par exemple avec SIGKILL); qui peut laisser traîner des données sensibles.
Mettre à jour:
Voici un petit utilitaire qui implémente l'approche de l'espace de noms. Il doit être compilé avec
cc -Wall -Os -s chtmp.c -o chtmp
et CAP_SYS_ADMIN
des capacités de fichier données (en tant que root) avec
setcap CAP_SYS_ADMIN+ep chtmp
Lors de l'exécution (en tant qu'utilisateur normal) en tant qu'utilisateur
./chtmp command args ...
il partagera son espace de noms de système de fichiers, montera un système de fichiers tmpfs /proc/sysvipc
, y chdir et s'exécutera command
avec les arguments donnés. command
ne pas hériter des CAP_SYS_ADMIN
capacités.
Ce système de fichiers ne sera pas accessible à partir d'un autre processus non démarré command
, et il disparaîtra comme par magie (avec tous les fichiers qui ont été créés à l'intérieur) quand command
et ses enfants meurent, peu importe comment cela se produit. Notez que cela ne fait que partager l'espace de noms de montage - il n'y a pas de barrière dure entre command
et d'autres processus exécutés par le même utilisateur; ils pourraient toujours se faufiler à l'intérieur de son espace de noms via ptrace(2)
,/proc/PID/cwd
ou par d' autres moyens.
Le détournement de "l'inutile" /proc/sysvipc
est, bien sûr, idiot, mais l'alternative aurait été de spam /tmp
avec des répertoires vides qui devraient être supprimés ou compliquer grandement ce petit programme avec des fourchettes et des attentes. Alternativement, dir
peut être changé par exemple. /mnt/chtmp
et le faire créer par root lors de l'installation; ne le rendez pas configurable par l'utilisateur et ne le définissez pas sur un chemin appartenant à l'utilisateur, car cela pourrait vous exposer à des pièges de lien symbolique et à d'autres trucs poilus qui ne valent pas la peine d'être consacrés.
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
work, shell est toujours dans ce répertoire. Mais aucun nouveau fichier ne peut être placé dans ce "dossier". Vous ne pouvez que lire / écrire avec le fichier & 3, & 4. Il s'agit donc toujours d'un "fichier temporaire" et non d'un "dossier temporaire".
Avez-vous besoin d'un shell spécifique?
Si zsh est une option, veuillez lire zshexpn(1)
:
Si = (...) est utilisé à la place de <(...), le fichier passé en argument sera le nom d'un fichier temporaire contenant la sortie du processus de liste. Ceci peut être utilisé à la place du formulaire <pour un programme qui s'attend à
lseek
(voirlseek(2)
) sur le fichier d'entrée.[...]
Un autre problème survient chaque fois qu'un travail avec une substitution qui nécessite un fichier temporaire est désavoué par le shell, y compris le cas où
&!
ou&|
apparaît à la fin d'une commande contenant une substitution. Dans ce cas, le fichier temporaire ne sera pas nettoyé car le shell n'a plus de mémoire du travail. Une solution de contournement consiste à utiliser un sous-shell, par exemple,(mycmd =(myoutput)) &!
car le sous-shell forké attendra la fin de la commande, puis supprimez le fichier temporaire.
Une solution de contournement générale pour garantir qu'une substitution de processus dure pendant une durée appropriée consiste à la transmettre en tant que paramètre à une fonction shell anonyme (un morceau de code shell exécuté immédiatement avec l'étendue de la fonction). Par exemple, ce code:
() { print File $1: cat $1 } =(print This be the verse)
produit quelque chose qui ressemble à ce qui suit
File /tmp/zsh6nU0kS: This be the verse
Par exemple, j'utilise ceci dans rifle (qui fait partie du gestionnaire de fichiers ranger) pour décrypter un fichier, puis exécuter rifle sur le fichier temporaire, qui est supprimé lorsque les sous-processus se terminent. (n'oubliez pas de régler $TERMCMD
)
# ~/.config/ranger/rifle.conf
...
!ext exe, mime octet-stream$, has gpg, flag t = () { rifle -f F "$1" } =(gpg -dq "$1")