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 a saída do shell?
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 a saída do shell?
Respostas:
No caso de um arquivo temporário, seu exemplo na pergunta o criaria, depois o desvincularia do diretório (fazendo com que "desapareça") e, quando o script fechar o filedescriptor (provavelmente após a finalização), o espaço ocupado pelo arquivo seria recuperável pelo sistema. Essa é 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 nenhuma maneira que tornaria o diretório utilizável.
Uma maneira comum de excluir arquivos e diretórios temporários no final de um script é instalando uma EXIT
armadilha de limpeza . Os exemplos de código fornecidos abaixo evitam ter que manipular completamente os filedescriptors.
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.
A EXIT
interceptação não será executada ao receber o KILL
sinal (que não pode ser interceptado), o que significa que não haverá limpeza realizada. No entanto, ele será executado ao terminar devido a um sinal INT
ou TERM
(se estiver sendo executado com bash
ou ksh
, em outros shells, você poderá adicionar esses sinais depois EXIT
na trap
linha de comando) ou quando sair normalmente devido à chegada ao final do script ou à execução de um exit
ligar.
.
e ..
. (Testado em Linux, eu não sei se isso é consistente em todas as plataformas.)
exec another-command
obviamente.
Escreva uma função shell que será executada quando o seu script se terminar. No exemplo abaixo, chamo de 'limpeza' e defino uma armadilha a 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.
cleanup
antes de uma saída limpa (0) e ao receber SIGHUP (1), SIGINT (2), SIGQUIT (3) e SIGABRT (6). ele irá não executar cleanup
quando as saídas de script por causa de SIGTERM, SIGSEGV, SIGKILL, SIGPIPE, etc. Isto é claramente deficiente.
Você pode chdir e removê-lo, desde que não tente usar os caminhos dentro dele posteriormente:
#! /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
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 jogar com um espaço para nome separado e mount -t tmpfs tmpfs /dir
dentro dele.
As respostas canônicas (coloque uma armadilha em EXIT) não funcionam se seu script for forçado a uma saída impura (por exemplo, com SIGKILL); que podem deixar dados confidenciais por aí.
Atualizar:
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 determinados CAP_SYS_ADMIN
recursos de arquivo (como root) com
setcap CAP_SYS_ADMIN+ep chtmp
Quando executar um usuário (normalmente) como
./chtmp command args ...
ele irá compartilhar seu espaço de nome do sistema de arquivos, montar um sistema de arquivos tmpfs /proc/sysvipc
, chdir nele e executar command
com os argumentos fornecidos. command
vai não herdar as CAP_SYS_ADMIN
capacidades.
Esse sistema de arquivos não estará acessível a partir de outro processo não iniciado 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 está apenas compartilhando o espaço para nome da montagem - não há barreiras rígidas entre command
e outros processos executados pelo mesmo usuário; eles ainda poderiam deslocar dentro de seu namespace quer através ptrace(2)
, /proc/PID/cwd
ou por outros meios.
O seqüestro do "inútil" /proc/sysvipc
é, é claro, bobo, mas a alternativa seria enviar spam /tmp
com diretórios vazios que precisariam ser removidos ou complicar bastante esse pequeno programa com garfos e esperas. Como alternativa, dir
pode ser alterado para, por exemplo. /mnt/chtmp
e crie-o pela raiz na instalação; não faça com que seja configurável pelo usuário e não o configure como um caminho de propriedade do usuário, pois isso pode expô-lo a desvios de links e outras coisas peludas 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);
}
rm $PWD
trabalho, shell ainda está nesse diretório. Mas nenhum novo arquivo pode ser colocado nesta "pasta". Somente você pode fazer é ler / gravar com o arquivo & 3, & 4. Portanto, este ainda é "arquivo temporário", não "pasta temporária".
Você precisa de um shell específico?
Se zsh for uma opção, leia zshexpn(1)
:
Se = (...) for usado em vez de <(...), o arquivo passado como argumento será o nome de um arquivo temporário que contém a saída do processo de lista. Isso pode ser usado em vez do formulário <para um programa que espera
lseek
(consultelseek(2)
) no arquivo de entrada.[...]
Outro problema surge sempre que um trabalho com uma substituição que requer um arquivo temporário é rejeitado pelo shell, incluindo o caso em que
&!
ou&|
aparece no final de um comando que contém uma substituição. Nesse caso, o arquivo temporário não será limpo, pois o shell não terá mais memória do trabalho. Uma solução alternativa é usar um subshell, por exemplo,(mycmd =(myoutput)) &!
pois o subshell bifurcado aguardará o término do comando e removerá o arquivo temporário.
Uma solução geral para garantir que uma substituição de processo perdure por um período de tempo apropriado é transmiti-la como um parâmetro para uma função de shell anônima (uma parte do código de shell que é executada imediatamente com o escopo da função). Por exemplo, este código:
() { print File $1: cat $1 } =(print This be the verse)
produz algo semelhante ao seguinte
File /tmp/zsh6nU0kS: This be the verse
Por exemplo, eu uso isso no rifle (parte do gerenciador de arquivos do ranger) para descriptografar um arquivo e depois executar o rifle no arquivo temporário, que é excluído quando o subprocesso termina. (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")