Pasta temporária que é destruída automaticamente após a saída do processo

10

Podemos usar pastas temporárias como arquivos temporários

TMP=$(mktemp ... )
exec 3<>$TMP
rm $TMP

cat <&3

que será destruído automaticamente após esta saída do shell?

    
por Bob Johnson 07.11.2018 / 11:44

4 respostas

12

No caso de um arquivo temporário, seu exemplo na pergunta o criaria, depois o desvinculará do diretório (fazendo com que ele "desapareça") e, quando o script fechar o filodescriptor (provavelmente após a rescisão), o espaço ocupado pelo arquivo seria recuperável pelo sistema. Esta é uma maneira comum de lidar com arquivos temporários em idiomas como C.

É, até onde eu sei, não é possível abrir um diretório da mesma maneira, pelo menos de forma alguma que torne o diretório utilizável.

Uma maneira comum de excluir arquivos e diretórios temporários no encerramento de um script é instalando uma limpeza EXIT trap. Os exemplos de código fornecidos abaixo evitam ter que manipular completamente os descritores de arquivo.

tmpdir=$(mktemp -d)
tmpfile=$(mktemp)

trap 'rm -f "$tmpfile"; rm -rf "$tmpdir"' EXIT

# The rest of the script goes here.

Ou você pode chamar uma função de limpeza:

cleanup () {
    rm -f "$tmpfile"
    rm -rf "$tmpdir"
}

tmpdir=$(mktemp -d)
tmpfile=$(mktemp)

trap cleanup EXIT

# The rest of the script goes here.

O EXIT trap não será executado ao receber o sinal KILL (que não pode ser interceptado), o que significa que não haverá limpeza executada. No entanto, ele será executado ao terminar devido a um sinal INT ou TERM (se estiver executando com bash ou ksh , em outros shells você pode adicionar esses sinais após EXIT na linha de comando trap ), ou ao sair normalmente devido a chegar ao final do script ou executar uma chamada exit .

    
por 07.11.2018 / 12:06
6

Escreva uma função de shell que será executada quando seu script terminar. No exemplo abaixo eu chamo de 'limpeza' e defino uma armadilha para ser executada nos níveis de saída, como: 0 1 2 3 6

trap cleanup 0 1 2 3 6

cleanup()
{
  [ -d $TMP ] && rm -rf $TMP
}

Veja este post para mais informações.

    
por 07.11.2018 / 11:57
6

Você pode entrar nela e depois removê-la, desde que você não tente usar caminhos dentro dela depois:

#! /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

Eu não verifiquei, mas provavelmente é o mesmo problema ao usar o openat (2) em C com um diretório que não existe mais no sistema de arquivos.

Se você é root e no Linux, pode brincar com um namespace separado e mount -t tmpfs tmpfs /dir dentro dele.

As respostas canônicas (definir uma armadilha no EXIT) não funcionam se o seu script for forçado a uma saída impura (por exemplo, com SIGKILL); que pode deixar dados confidenciais por aí.

Atualização:

Aqui está um pequeno utilitário que implementa a abordagem de namespace. Deve ser compilado com

cc -Wall -Os -s chtmp.c -o chtmp

e dados os recursos do arquivo CAP_SYS_ADMIN (como root) com

setcap CAP_SYS_ADMIN+ep chtmp

Quando o usuário é executado (como normal) como

./chtmp command args ...

ele descompactará seu namespace do sistema de arquivos, montará um sistema de arquivos tmpfs em /proc/sysvipc , entrará nele e executará command com os argumentos fornecidos. command não herdará os recursos CAP_SYS_ADMIN .

Esse sistema de arquivos não estará acessível a partir de outro processo não iniciado a partir de command , e desaparecerá magicamente (com todos os arquivos criados dentro dele) quando command e seus filhos morrerem, não importa como isso aconteça. Observe que isso é apenas o não compartilhamento do namespace de montagem - não há barreiras rígidas entre command e outros processos executados pelo mesmo usuário; eles ainda podem se infiltrar em seu namespace por meio de ptrace(2) , /proc/PID/cwd ou por outros meios.

O seqüestro do "inútil" /proc/sysvipc é, claro bobagem, mas a alternativa teria sido o spam /tmp com diretórios vazios que teriam que ser removidos ou complicar muito esse pequeno programa com garfos e esperas . Alternativamente, dir pode ser alterado para, por exemplo. /mnt/chtmp e tê-lo criado pelo root na instalação; não o torne configurável pelo usuário e não o defina como um caminho de propriedade do usuário, pois isso pode expô-lo a armadilhas de links simbólicos e outras coisas que não valem a pena gastar tempo.

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);
}
    
por 07.11.2018 / 13:27
0

Você precisa de um shell específico?

Se zsh for uma opção, leia zshexpn(1) :

If =(...) is used instead of <(...), then the file passed as an argument will be the name of a temporary file containing the output of the list process. This may be used instead of the < form for a program that expects to lseek (see lseek(2)) on the input file.

[...]

Another problem arises any time a job with a substitution that requires a temporary file is disowned by the shell, including the case where &! or &| appears at the end of a command containing a substitution. In that case the temporary file will not be cleaned up as the shell no longer has any memory of the job. A workaround is to use a subshell, for example,

(mycmd =(myoutput)) &!

as the forked subshell will wait for the command to finish then remove the temporary file.

A general workaround to ensure a process substitution endures for an appropriate length of time is to pass it as a parameter to an anonymous shell function (a piece of shell code that is run immediately with function scope). For example, this code:

() {
   print File $1:
   cat $1
} =(print This be the verse)

outputs something resembling the following

File /tmp/zsh6nU0kS:
This be the verse

Por exemplo, eu uso isso no rifle (parte do gerenciador de arquivos ranger) para descriptografar um arquivo e, em seguida, executar o rifle no arquivo temporário, que é excluído quando os subprocessos terminam. (não se esqueça de definir $TERMCMD )

# ~/.config/ranger/rifle.conf
...
!ext exe, mime octet-stream$, has gpg, flag t = () { rifle -f F "$1" } =(gpg -dq "$1")
    
por 14.11.2018 / 08:36