Criando arquivos temporários no bash

150

Existem maneiras objetivamente melhores de criar arquivos temporários em scripts bash?

Normalmente, apenas nomeio o que quer que me vem à cabeça, como tempfile-123, pois será excluído quando o script terminar. Existe alguma desvantagem em fazer isso além de substituir um possível tempfile-123 na pasta atual? Ou existe alguma vantagem em criar um arquivo temporário de uma maneira mais cuidadosa?

Strapakowsky
fonte
1
Não use arquivos temporariamente. Use diretórios temporariamente. E não use mktemp. Veja aqui o porquê: codeproject.com/Articles/15956/…
ceving
17
@ceving Esse artigo está simplesmente errado, pelo menos quando aplicado ao comando mktemp do shell (em oposição à chamada da biblioteca mktemp). Como o mktemp cria o próprio arquivo com uma umask restritiva, o ataque dado funciona apenas se o atacante estiver operando na mesma conta que o atacante ... nesse caso, o jogo já está perdido. Para as melhores práticas no mundo do shell-scripting, consulte mywiki.wooledge.org/BashFAQ/062
Charles Duffy
Você também pode usar tempfile(1)em sistemas que o possuem.
Pausado até novo aviso.

Respostas:

179

A mktemp(1)página do manual explica bastante bem:

Tradicionalmente, muitos scripts de shell tomam o nome do programa com o pid como sufixo e o usam como um nome de arquivo temporário. Esse tipo de esquema de nomeação é previsível e a condição de corrida criada é fácil para um atacante vencer. Uma abordagem mais segura, embora ainda inferior, é criar um diretório temporário usando o mesmo esquema de nomenclatura. Embora isso permita garantir que um arquivo temporário não seja subvertido, ele ainda permite um simples ataque de negação de serviço. Por esses motivos, sugere-se que o mktemp seja usado.

Em um script, invoco mktemp algo como

mydir=$(mktemp -d "${TMPDIR:-/tmp/}$(basename $0).XXXXXXXXXXXX")

que cria um diretório temporário no qual posso trabalhar e no qual posso nomear com segurança os arquivos reais como legíveis e úteis.

mktempnão é padrão, mas existe em muitas plataformas. Os "X" s geralmente serão convertidos em alguma aleatoriedade, e mais provavelmente serão mais aleatórios; no entanto, alguns sistemas (cinza do busybox, por um) limitam essa aleatoriedade mais significativamente do que outros


A propósito, a criação segura de arquivos temporários é importante para mais do que apenas scripts de shell. É por isso que python possui tempfile , perl possui File :: Temp , ruby ​​possui Tempfile , etc…

kojiro
fonte
1
A opção -t está obsoleta: gnu.org/software/coreutils/manual/html_node/…
Tibor Vass
7
Parece que a maneira mais segura e multiplataforma de usar mktempé combinada com basename, dessa forma mktemp -dt "$(basename $0). XXXXXXXXXX". Se usado sem basenamevocê, poderá receber um erro como este modelo mktemp: invalid, `/tmp/MOB-SAN-JOB1-183-ScriptBuildTask-7300464891856663368.sh.XXXXXXXXXX ', contém separador de diretório .
I4niac 28/05
7
Ignore o erro de digitação (espaço extra). mktemp -dt "$(basename $0).XXXXXXXXXX"é o caminho correto.
I4niac
3
@ i4niac: você precisa de citar que $0, há espaços a abundância na terra do OS X.mktemp -dt "$(basename "$0").XXXXXX"
Orwellophile
11
Também pode ser bom remover o tempdir no final da execução do script:trap "rm -rf $mydir" EXIT
KumZ 25/07/16
43

Sim, use mktemp .

Ele criará um arquivo temporário dentro de uma pasta projetada para armazenar arquivos temporários e garantirá um nome exclusivo. Ele gera o nome desse arquivo:

> mktemp
/tmp/tmp.xx4mM3ePQY
>
Paulo
fonte
19

Você pode querer olhar mktemp

O utilitário mktemp pega o modelo de nome de arquivo especificado e sobrescreve uma parte dele para criar um nome de arquivo exclusivo. O modelo pode ser qualquer nome de arquivo com algum número de 'Xs anexado a ele, por exemplo /tmp/tfile.XXXXXXXXXX. Os 'Xs' à direita são substituídos por uma combinação do número do processo atual e letras aleatórias.

Para mais detalhes: man mktemp

John Lawrence
fonte
11

Existe alguma vantagem em criar um arquivo temporário de uma maneira mais cuidadosa

Os arquivos temporários geralmente são criados no diretório temporário (como /tmp), onde todos os outros usuários e processos têm acesso de leitura e gravação (qualquer outro script pode criar os novos arquivos). Portanto, o script deve ter cuidado ao criar os arquivos, como usar com as permissões certas (por exemplo, somente leitura para o proprietário, consulte help umask:) e o nome do arquivo não deve ser facilmente adivinhado (idealmente aleatório). Caso contrário, se os nomes de arquivos não forem exclusivos, ele poderá criar conflito com o mesmo script executado várias vezes (por exemplo , condição de corrida) ou algum invasor pode seqüestrar algumas informações confidenciais (por exemplo, quando as permissões são muito abertas e o nome do arquivo é fácil de adivinhar) ou criar / substituir o arquivo por sua própria versão do código (como substituir os comandos ou consultas SQL, dependendo do que está sendo armazenado).


Você pode usar a seguinte abordagem para criar o diretório temporário:

TMPDIR=".${0##*/}-$$" && mkdir -v "$TMPDIR"

ou arquivo temporário:

TMPFILE=".${0##*/}-$$" && touch "$TMPFILE"

No entanto, ainda é previsível e não é considerado seguro.

De acordo com man mktemp, podemos ler:

Tradicionalmente, muitos scripts de shell tomam o nome do programa com o pid como sufixo e o usam como um nome de arquivo temporário. Esse tipo de esquema de nomeação é previsível e a condição de corrida criada é fácil para um atacante vencer.

Para estar seguro, é recomendável usar o mktempcomando para criar um arquivo ou diretório temporário exclusivo ( -d).

kenorb
fonte
2
não exatamente o que foi perguntado. No entanto, pode ser uma solução perfeita.
Jpbochi
1
@jpbochi Melhorei a resposta para resolver a questão. Deixe-me saber se isso ajuda.
kenorb
2
Isso realmente melhora a resposta. Meu voto já era seu, no entanto. Não posso votar mais. Uma sugestão que tenho é explicar o que ${0##*/}e $$expandir ou vincular a alguma documentação sobre isso.
jpbochi
0

mktemp é provavelmente o mais versátil, especialmente se você planeja trabalhar com o arquivo por um tempo.

Você também pode usar um operador de substituição de processo <() se precisar apenas temporariamente do arquivo como entrada para outro comando, por exemplo:

$ diff <(echo hello world) <(echo foo bar)
Barry Jones
fonte