Como criar um arquivo temporário no shell script?

155

Ao executar um script, quero criar um arquivo temporário no /tmpdiretório

Após a execução desse script, isso será limpo por esse script.

Como fazer isso no shell script?

Bhuvanesh
fonte

Respostas:

198
tmpfile=$(mktemp /tmp/abc-script.XXXXXX)
: ...
rm "$tmpfile"

Você pode garantir que um arquivo seja excluído quando o script sair (incluindo mortes e falhas), abrindo um descritor de arquivo e excluindo-o. O arquivo permanece disponível (para o script; não para outros processos, mas /proc/$PID/fd/$FDé uma solução alternativa), desde que o descritor de arquivo esteja aberto. Quando é fechado (o que o kernel faz automaticamente quando o processo termina), o sistema de arquivos exclui o arquivo.

tmpfile=$(mktemp /tmp/abc-script.XXXXXX)
exec 3>"$tmpfile"
rm "$tmpfile"
: ...
echo foo >&3
Hauke ​​Laging
fonte
4
Boa resposta, solução elegante com o descritor de arquivo em caso de falha +1
caos
2
/proc- exceto para sistemas que não o possuem.
Dennis Williamson
4
o que exec 3> "$tmpfile"faz? Isso não é útil apenas se o tmpfile for um script independente?
Alexej Magura
5
Como você lê o FD criado?
Eckes
3
"Você pode usar gato <3 ou algo semelhante." na verdade, que lê de um arquivo chamado 3 @ dragon788. Além disso, cat <&3vai dar Bad file descriptor. Eu apreciaria se você consertar ou remover; desinformação não ajuda muito.
Daniel Farrell
65

Use mktemppara criar um arquivo ou diretório temporário:

temp_file=$(mktemp)

Ou para um direcotry:

temp_dir=$(mktemp -d)

No final do script, você deve excluir o arquivo / diretório temporário:

rm ${temp_file}
rm -R ${temp_dir}

mktemp cria um arquivo no /tmpdiretório ou no diretório fornecido com o --tmpdirargumento

caos
fonte
20
Você pode usar trap "rm -f $temp_file" 0 2 3 15logo após criar o arquivo para que, quando o script sair ou for interrompido, ctrl-Co arquivo ainda seja removido.
wurtel
1
@wurtel O que acontece se EXITfor o único gancho trap?
Hauke ​​Laging
4
@HaukeLaging A armadilha não dispara se o script for interrompido com Ctrl + C. Uma coisa a notar é que o TRAP não ajuda se você kill -9 $somepid. Esse sinal de matança em particular é a morte instantânea e nada mais acontece.
precisa saber é o seguinte
5
@ dragon788 Você já tentou isso? Você deve. bash -c 'echo $$; trap "echo foo" 0; sleep 5'
Hauke ​​Laging
Armadilha EXITé suficiente.
Kusalananda
15

Se você estiver no sistema que possui mktemp , use-o como outras respostas.

Com o suporte de ferramenta POSIX:

umask 0177
tmpfile=/tmp/"$0"."$$"."$(awk 'BEGIN {srand();printf "%d\n", rand() * 10^10}')"
trap 'rm -f -- "$tmpfile"' INT TERM HUP EXIT
: > "$tmpfile"
cuonglm
fonte
O que acontece se EXITfor o único gancho trap?
Hauke ​​Laging
@HaukeLaging: tmpfileainda será removido antes da saída do script, mas não quando o script receber outros sinais.
precisa saber é
Não é o que acontece aqui (GNU bash, versão 4.2.53).
Hauke ​​Laging 30/01/15
@HaukeLaging: Como assim That's not what happens?
precisa saber é
3
mktemporiginado no HP / UX com uma sintaxe diferente. Todd C. Miller criou um diferente para o OpenBSD em meados dos anos 90 (copiado pelo FreeBSD e NetBSD) e posteriormente o disponibilizou como um utilitário independente (www.mktemp.org). Esse é o que normalmente era usado no Linux até que um mktemputilitário (principalmente compatível) foi adicionado aos coreutils do GNU em 2007. Apenas para dizer que não se pode realmente dizer que mktempé um utilitário do GNU.
Stéphane Chazelas
14

Algumas conchas têm o recurso embutido.

zsh

zshA =(...)forma de substituição do processo usa um arquivo temporário. Por exemplo, =(echo test)expande para o caminho de um arquivo temporário que contém test\n.

$ {cat $file; ls -l /dev/fd/3; echo test2 >&3; cat $file} 3<> ${file::==(echo test)}
test
lrwx------ 1 stephane stephane 64 Jan 30 11:19 /dev/fd/3 -> /tmp/zshMLbER0
test2

Esse arquivo é removido automaticamente quando o comando é concluído.

bash / zsh no Linux.

Here-files ou here-strings bashe zshsão implementados como arquivos temporários excluídos.

Então, se você fizer:

exec 3<<< test

O descritor de arquivo 3 está conectado a um arquivo temporário excluído que contém test\n.

Você pode obter seu conteúdo com:

cat <&3

Se no Linux, você também pode ler ou gravar nesse arquivo via /dev/fd/3

$ exec 3<<< test
$ cat <&3
test
$ echo foo > /dev/fd/3
$ cat /dev/fd/3
foo

(algumas outras conchas usam tubos ou podem ser usadas /dev/nullse o documento aqui estiver vazio).

POSIX

Não há mktemputilitário POSIX. No entanto, o POSIX especifica uma mkstemp(template)API C e o m4utilitário padrão expõe essa API com a mkstemp()função m4 com o mesmo nome.

mkstemp()fornece um nome de arquivo com uma parte aleatória que foi garantida para não existir no momento em que a função foi chamada. Ele cria o arquivo com permissões 0600 de forma livre de corrida.

Então, você poderia fazer:

tmpfile=$(
  echo 'mkstemp(template)' |
    m4 -D template="${TMPDIR:-/tmp}/baseXXXXXX"
) || exit

Observe, no entanto, que você precisa lidar com a limpeza na saída, embora, se você apenas precise escrever e ler o arquivo um número fixo de vezes, poderá abri-lo e excluí-lo logo após criar o arquivo para aqui-doc / aqui- abordagem de string acima:

tmpfile=$(
  echo 'mkstemp(template)' |
    m4 -D template="${TMPDIR:-/tmp}/baseXXXXXX"
) || exit

# open once for writing, twice for reading:
exec 3> "$tempfile" 4< "$tempfile" 5< "$tempfile"

rm -f -- "$tmpfile"

cmd >&3   # store something in the temp file
exec 3>&- # fd no longer needed

# read the content twice:
cat <&4
cat <&5

Você pode abrir o arquivo para leitura uma vez e retroceder entre duas leituras, no entanto, não existe um utilitário POSIX que possa fazer isso retroceder ( lseek()); portanto, não é possível fazê-lo de forma portável em um script POSIX ( zsh( sysseekincorporado) e ksh93( <#((...))operador) pode faça isso).

Stéphane Chazelas
fonte
1
Bash também tem substituição de processo usando<()
WinnieNicklaus 30/01
3
@WinnieNicklaus, sim, mas como não usa arquivos temporários, é irrelevante aqui. Substituição processo foi introduzido por ksh, copiado por bash e zsh, e zsh estendeu-lo com uma terceira forma: =(...).
Stéphane Chazelas
7

Aqui está uma resposta um pouco melhorada na linha de Hauke ​​Laging:

#!/bin/bash

tmpfile=$(mktemp)  # Create a temporal file in the default temporal folder of the system

# Lets do some magic for the tmpfile to be removed when this script ends, even if it crashes
exec {FD_W}>"$tmpfile"  # Create file descriptor for writing, using first number available
exec {FD_R}<"$tmpfile"  # Create file descriptor for reading, using first number available
rm "$tmpfile"  # Delete the file, but file descriptors keep available for this script

# Now it is possible to work with the temporal file
echo foo >&$FD_W
echo bar >&$FD_W  # Note that file descriptor always concatenates, not overwrites

cat <&$FD_R
SwanS
fonte
2
Note-se que o conteúdo está disponível apenas uma vez. Ou seja, se eu fizer cat <& $ FD_R pela segunda vez, nenhuma saída será produzida. Consulte unix.stackexchange.com/questions/166482/… . Existe alguma maneira de excluir automaticamente o arquivo se o programa travar, mas torná-lo acessível várias vezes?
smihael
0

Meu fluxo de trabalho normalmente com arquivos temporários é devido a algum script bash que estou testando. Eu quero fazer teeisso para que eu possa ver que está funcionando e salvar a saída para a próxima iteração do meu processo. Eu criei um arquivo chamadotmp

#!/bin/bash
echo $(mktemp /tmp/$(date +"%Y-%m-%d_%T_XXXXXX"))

para que eu possa usá-lo como

$ some_command --with --lots --of --stuff | tee $(tmp)

A razão pela qual eu gosto da data e hora formatada antes dos valores aleatórios é que me permite encontrar o arquivo tmp que acabei de criar com facilidade, e não preciso pensar em como nomeá-lo na próxima vez (e focar apenas em obter meu script dang trabalhar).

Frank Bryce
fonte