Arquivos temporários seguros e diretórios de bloqueio
Use mktemp e flock para criar recursos temporários sem condições de corrida e impedir execuções simultâneas de scripts.
Arquivos temporários seguros e diretórios de bloqueio é uma aula grátis de DevOps Bootcamp no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de DevOps Bootcamp, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de DevOps Bootcamp inclui 4 aulas no total.
Por que arquivos temporários são um risco de segurança
Os scripts Bash frequentemente precisam de armazenamento temporário — resultados intermediários, marcadores de bloqueio e áreas de preparação. Mas criar arquivos temporários de forma descuidada abre vulnerabilidades sérias.
- Condições de corrida: outro processo pode prever seu nome de arquivo e criar o arquivo primeiro, redirecionando suas gravações.
- Ataques por links simbólicos: um invasor cria um link simbólico no caminho esperado, apontando para um arquivo sensível como
/etc/passwd. - Arquivos deixados para trás: se um script falhar, os arquivos temporários se acumulam e podem expor dados sensíveis.
As duas ferramentas principais que eliminam esses problemas são mktemp e flock. Nesta lição, você aprenderá a usar ambas com segurança e de forma defensiva.
Criando arquivos temporários seguros com mktemp
mktemp cria um arquivo temporário com um nome aleatório e imprevisível e retorna seu caminho. Ele cria o arquivo atomicamente, portanto nenhum outro processo pode obter o nome primeiro.
- Sintaxe:
mktemp [TEMPLATE]— o modelo deve terminar com pelo menos três caracteresX. - Cada
Xé substituído por um caractere aleatório, produzindo um nome único como/tmp/script.aB3kQz. - O arquivo é criado automaticamente com as permissões
0600(legível somente pelo proprietário).
Sempre capture imediatamente o caminho retornado em uma variável para que você possa referenciá-lo e removê-lo posteriormente.
#!/usr/bin/env bash
set -euo pipefail
# Create a secure temp file
TMPFILE=$(mktemp /tmp/myapp.XXXXXX)
echo "Temp file created at: $TMPFILE"
# Write data to it
echo "some intermediate result" > "$TMPFILE"
# Read it back
cat "$TMPFILE"
# Clean up
rm -f "$TMPFILE"Sempre faça a limpeza com uma armadilha
Se o script for encerrado inesperadamente — devido a um erro, a um sinal ou ao acionamento de set -e —, os arquivos temporários permanecerão no sistema, a menos que você registre um manipulador de limpeza.
O comando integrado trap executa um comando quando o shell recebe um sinal ou é encerrado. O padrão consagrado para a limpeza de arquivos temporários é:
- Registre a armadilha imediatamente depois de criar o arquivo temporário.
- Registre a armadilha em
EXITpara que a limpeza seja executada tanto no encerramento normal quanto no anormal. - Registre também as armadilhas
INTeTERMse o script for demorado ou interativo.
Isso garante que não haja arquivos órfãos, mesmo que o script seja encerrado durante a execução.
#!/usr/bin/env bash
set -euo pipefail
TMPFILE=$(mktemp /tmp/report.XXXXXX)
# Register cleanup before doing any real work
cleanup() {
rm -f "$TMPFILE"
echo "Cleaned up $TMPFILE" >&2
}
trap cleanup EXIT
# Do work — even if this fails, cleanup() will run
echo "Processing..." > "$TMPFILE"
grep "result" "$TMPFILE" || true
echo "Done. File will be removed on exit."Criando diretórios temporários com mktemp -d
Às vezes, é necessário ter um diretório inteiro para preparar vários arquivos — por exemplo, ao criar um arquivo compactado ou extrair um tarball antes do processamento. Use mktemp -d para criar um diretório temporário seguro.
- O diretório é criado com as permissões
0700(acesso somente do proprietário). - Faça a limpeza com
rm -rfno seu trap — tenha cuidado para remover somente a variável, nunca um caminho fixo. - Use aspas duplas e verifique se a variável não está vazia antes de chamar
rm -rf, como uma verificação de segurança adicional.
#!/usr/bin/env bash
set -euo pipefail
TMPDIR=$(mktemp -d /tmp/extract.XXXXXX)
cleanup() {
# Guard: only rm if variable is set and non-empty
[[ -n "${TMPDIR:-}" ]] && rm -rf "$TMPDIR"
}
trap cleanup EXIT
echo "Working in $TMPDIR"
# Simulate staging files
echo "file one" > "$TMPDIR/part1.txt"
echo "file two" > "$TMPDIR/part2.txt"
ls "$TMPDIR"
echo "All done."O problema das execuções simultâneas de scripts
Tarefas do Cron, temporizadores do systemd e scripts acionados manualmente podem iniciar facilmente várias instâncias do mesmo script ao mesmo tempo. Isso causa:
- Processamento duplicado: Os mesmos registros do banco de dados ou arquivos são processados duas vezes.
- Saída corrompida: Duas instâncias escrevem simultaneamente no mesmo arquivo de saída.
- Deadlocks ou estado parcial: Ambas as instâncias modificam recursos compartilhados em uma ordem intercalada e imprevisível.
A solução tradicional era gravar um arquivo PID e verificá-lo na inicialização — mas essa abordagem apresenta uma janela de condição de corrida entre a verificação e a gravação. A solução moderna correta é flock, que usa o mecanismo de bloqueio consultivo do kernel para garantir um bloqueio sem condições de corrida.
Bloqueio com flock: o padrão de uma linha
flock adquire um bloqueio consultivo em um descritor de arquivo antes de executar um comando. O uso mais simples envolve todo o seu script a partir da linha de comando:
flock -n /var/lock/myscript.lock bash myscript.sh
-n(não bloqueante): encerra imediatamente com o status 1 se o bloqueio já estiver mantido, em vez de aguardar.- Sem
-n,flockbloqueia até que o bloqueio fique disponível — útil para enfileiramento. - O próprio arquivo de bloqueio é apenas um marcador; seu conteúdo não importa. É seguro mantê-lo entre as execuções.
- Quando o processo que mantém o bloqueio é encerrado, o kernel o libera automaticamente — não é necessária nenhuma limpeza manual.
#!/usr/bin/env bash
# launcher.sh — prevents concurrent runs of worker.sh
set -euo pipefail
LOCKFILE="/tmp/myworker.lock"
if ! flock -n "$LOCKFILE" bash -c 'echo "Running worker..."; sleep 2; echo "Done."'; then
echo "Another instance is already running. Exiting." >&2
exit 1
fiflock dentro de um script usando um descritor de arquivo
Para fazer o bloqueio dentro de um script, em vez de envolvê-lo externamente, use exec para abrir um descritor de arquivo e, em seguida, chame flock nesse descritor. Esse é o padrão idiomático usado em scripts de produção.
exec 200>"$LOCKFILE"abre o arquivo no descritor 200 para gravação (criando-o se necessário).flock -n 200tenta bloquear o descritor 200 sem bloquear.- Como o bloqueio está vinculado ao descritor de arquivo (não ao nome do arquivo), ele é liberado automaticamente quando o processo do shell é encerrado.
- Os números de descritor de 200 a 299 são usados convencionalmente para evitar conflitos com stdin/stdout/stderr.
#!/usr/bin/env bash
set -euo pipefail
LOCKFILE="/tmp/myjob.lock"
# Open lock file on FD 200
exec 200>"$LOCKFILE"
# Attempt non-blocking lock
if ! flock -n 200; then
echo "ERROR: Another instance of this script is running." >&2
exit 1
fi
echo "Lock acquired. Starting work..."
sleep 1
echo "Work complete. Lock will be released on exit."Combinando mktemp e flock em um único script
Scripts defensivos reais precisam dos dois recursos: um bloqueio para impedir execuções simultâneas e arquivos temporários seguros para dados intermediários. Veja o padrão completo que combina as duas técnicas:
- Adquira primeiro o bloqueio — antes de criar qualquer arquivo temporário — para que apenas uma instância execute qualquer trabalho.
- Crie os recursos temporários depois que o bloqueio for confirmado.
- Registre o
trapimediatamente após criar os recursos temporários, para que a limpeza seja garantida independentemente de como o script seja encerrado. - O arquivo de bloqueio nunca deve ficar no diretório temporário — ele precisa persistir entre as execuções para que
flockpossa referenciá-lo.
#!/usr/bin/env bash
set -euo pipefail
LOCKFILE="/tmp/report_builder.lock"
exec 200>"$LOCKFILE"
if ! flock -n 200; then
echo "Already running — aborting." >&2
exit 1
fi
# Now safe to create temp resources
TMPDIR=$(mktemp -d /tmp/report.XXXXXX)
TMPLOG=$(mktemp /tmp/report_log.XXXXXX)
cleanup() {
rm -rf "${TMPDIR:-}"
rm -f "${TMPLOG:-}"
}
trap cleanup EXIT
echo "Building report in $TMPDIR" | tee "$TMPLOG"
echo "Step 1 complete" >> "$TMPLOG"
cat "$TMPLOG"Diretórios de bloqueio como mecanismo alternativo
Em sistemas nos quais flock não está disponível (alguns sistemas embarcados ou sistemas de arquivos de rede, como NFS), você pode usar diretórios de bloqueio. mkdir é atômico em sistemas POSIX: ele só é bem-sucedido se o diretório ainda não existir.
- Crie o diretório de bloqueio com
mkdir /tmp/myscript.lock.d— se outra instância já o tiver criado,mkdirfalhará imediatamente. - Armazene metadados (como o PID) dentro do diretório para fins de diagnóstico.
- Sempre remova o diretório em um
trapparaEXIT. - Observação: Ao contrário de
flock, um bloqueio de diretório NÃO é liberado automaticamente se o processo for encerrado com-9ou se a máquina for reiniciada — adicione uma verificação para detectar bloqueios obsoletos.
#!/usr/bin/env bash
set -euo pipefail
LOCKDIR="/tmp/myscript.lock.d"
# Atomic mkdir — fails if directory already exists
if ! mkdir "$LOCKDIR" 2>/dev/null; then
# Check if the holding PID is still alive
HOLDER_PID=$(cat "$LOCKDIR/pid" 2>/dev/null || echo "")
if [[ -n "$HOLDER_PID" ]] && kill -0 "$HOLDER_PID" 2>/dev/null; then
echo "Locked by PID $HOLDER_PID. Exiting." >&2
exit 1
else
echo "Stale lock detected. Removing and continuing." >&2
rm -rf "$LOCKDIR"
mkdir "$LOCKDIR"
fi
fi
echo "$$" > "$LOCKDIR/pid"
trap 'rm -rf "$LOCKDIR"' EXIT
echo "Lock acquired via directory. Running..."
sleep 1
echo "Done."Espera com tempo limite usando flock
Às vezes, você deseja aguardar um bloqueio em vez de falhar imediatamente — mas não quer esperar para sempre. flock oferece suporte a um tempo limite com a opção -w.
flock -w 10 200aguarda até 10 segundos pelo bloqueio e, em seguida, encerra com o status 1 se ele ainda estiver indisponível.- Isso é ideal para scripts que devem aguardar na fila atrás de um processo predecessor de curta duração, mas desistir se esse processo estiver travado.
- Combine
-wcom uma mensagem de erro significativa que inclua o contexto — o caminho do arquivo de bloqueio e por quanto tempo você aguardou — para que os operadores possam diagnosticar travamentos rapidamente.
#!/usr/bin/env bash
set -euo pipefail
LOCKFILE="/tmp/data_sync.lock"
TIMEOUT=15
exec 200>"$LOCKFILE"
echo "Waiting up to ${TIMEOUT}s for lock on $LOCKFILE..."
if ! flock -w "$TIMEOUT" 200; then
echo "ERROR: Could not acquire lock after ${TIMEOUT}s." \
"Another instance may be hung." >&2
exit 1
fi
echo "Lock acquired. Syncing data..."
sleep 1
echo "Sync complete."Lista de verificação defensiva: recursos temporários seguros
Antes de distribuir qualquer script que use arquivos temporários ou bloqueios, percorra esta lista de verificação:
- Use
mktemp, nunca caminhos fixos —/tmp/myapp.tmpé previsível e pode ser explorado. - Capture o caminho imediatamente —
TMPFILE=$(mktemp ...)antes de qualquer outro comando. - Registre
trap cleanup EXITimediatamente após a criação — não no final do script. - Coloque aspas duplas em todos os usos de variáveis —
rm -f "$TMPFILE", nuncarm -f $TMPFILE. - Prefira
flocka arquivos PID — gerenciado pelo kernel e liberado automaticamente em caso de falha. - Use
-nnão bloqueante por padrão — bloqueios silenciosos que aguardam ocultam problemas de desempenho. - Coloque o arquivo de bloqueio fora do diretório temporário — para que ele sobreviva ao trap de limpeza.
- Teste o comportamento da limpeza — execute o script e aplique
kill -9durante a execução; verifique se não restam arquivos (em scripts baseados emflock; bloqueios de diretório exigem cuidado adicional).
Verificação de conhecimento: comportamento das opções de flock
Uma tarefa do Cron é executada a cada minuto e processa um arquivo compartilhado. Você deseja que qualquer nova invocação se encerre imediatamente com um erro se uma execução anterior ainda estiver ativa, sem aguardar. Qual invocação de flock implementa isso corretamente?
Recapitulação: arquivos temporários seguros e diretórios de bloqueio
Nesta lição, você aprendeu as duas ferramentas essenciais para o gerenciamento defensivo de recursos no Bash:
mktempcria arquivos temporários (0600) e diretórios (0700) com permissões seguras e nomes imprevisíveis, eliminando condições de corrida e ataques por links simbólicos que afetam caminhos fixos.trap cleanup EXITgarante a remoção de arquivos temporários em qualquer encerramento — normal, causado por erro ou causado por sinal — quando registrado imediatamente após a criação.flockfornece bloqueio consultivo imposto pelo kernel: use-npara falhar rapidamente em caso de contenção,-w Npara aguardar com um tempo limite e o padrãoexec 200>filepara bloqueio dentro do script, que o kernel libera automaticamente quando o processo é encerrado.- Diretórios de bloqueio (
mkdir) oferecem uma alternativa portátil para ambientes nos quaisflocknão está disponível, mas exigem detecção explícita de bloqueios obsoletos. - Mantenha sempre o arquivo de bloqueio fora do diretório temporário e coloque aspas duplas em todas as variáveis usadas na limpeza.
A combinação de mktemp + flock + trap produz scripts seguros contra invocações simultâneas, falhas imprevisíveis e manipulação mal-intencionada do sistema de arquivos.
Perguntas Frequentes
A aula “Arquivos temporários seguros e diretórios de bloqueio” é grátis?
Sim — o texto completo de “Arquivos temporários seguros e diretórios de bloqueio” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de DevOps Bootcamp, atualize para CoddyKit PRO. O curso de DevOps Bootcamp inclui 4 aulas no total.
O que vou aprender em “Arquivos temporários seguros e diretórios de bloqueio”?
Use mktemp e flock para criar recursos temporários sem condições de corrida e impedir execuções simultâneas de scripts. Você pratica DevOps Bootcamp com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar DevOps Bootcamp?
Nenhuma experiência prévia é necessária. DevOps Bootcamp no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.
Quanto tempo leva a aula “Arquivos temporários seguros e diretórios de bloqueio”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de DevOps Bootcamp?
Sim. Cada aula de DevOps Bootcamp inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Modo estrito com set -euo pipefail
- Manipuladores trap para limpeza e sinais
- Arquivos temporários seguros e diretórios de bloqueio
- Scripts idempotentes e lógica de novas tentativas com espera progressiva