Bezpieczne pliki tymczasowe i katalogi blokad
Używaj mktemp i flock do tworzenia zasobów tymczasowych odpornych na wyścigi oraz zapobiegania równoczesnemu uruchamianiu skryptów.
Bezpieczne pliki tymczasowe i katalogi blokad to bezpłatna lekcja Linux Command Line & Bash Scripting Mastery na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Linux Command Line & Bash Scripting Mastery, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Linux Command Line & Bash Scripting Mastery zawiera 4 lekcji w sumie.
Dlaczego pliki tymczasowe stanowią zagrożenie bezpieczeństwa
Skrypty Bash często potrzebują tymczasowego miejsca na dane — wyniki pośrednie, znaczniki blokad i obszary buforowe. Jednak nieostrożne tworzenie plików tymczasowych otwiera poważne luki w zabezpieczeniach.
- Wyścigi: inny proces może przewidzieć nazwę pliku i utworzyć go pierwszy, przekierowując zapisy.
- Ataki z użyciem dowiązań symbolicznych: atakujący tworzy dowiązanie symboliczne pod oczekiwaną ścieżką, wskazujące na wrażliwy plik, taki jak
/etc/passwd. - Pozostawione pliki: jeśli skrypt ulegnie awarii, pliki tymczasowe będą się gromadzić i mogą ujawnić poufne dane.
Dwoma podstawowymi narzędziami eliminującymi te problemy są mktemp i flock. W tej lekcji pokazano, jak używać obu bezpiecznie i defensywnie.
Bezpieczne tworzenie plików tymczasowych za pomocą mktemp
mktemp tworzy plik tymczasowy o losowej, nieprzewidywalnej nazwie i zwraca jego ścieżkę. Tworzy plik atomowo, więc żaden inny proces nie może wcześniej przejąć tej nazwy.
- Składnia:
mktemp [TEMPLATE]— szablon musi kończyć się co najmniej trzema znakamiX. - Każdy znak
Xjest zastępowany losowym znakiem, co tworzy unikatową nazwę, taką jak/tmp/script.aB3kQz. - Plik jest automatycznie tworzony z uprawnieniami
0600(może go odczytać tylko właściciel).
Zwróconą ścieżkę należy zawsze natychmiast zapisać w zmiennej, aby później móc się do niej odwołać i usunąć plik.
#!/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"Zawsze sprzątaj za pomocą trap
Jeśli skrypt nieoczekiwanie zakończy działanie — z powodu błędu, sygnału lub wywołania set -e — pliki tymczasowe pozostaną, chyba że zostanie zarejestrowana procedura obsługi sprzątania.
Wbudowane polecenie trap wykonuje polecenie, gdy powłoka otrzyma sygnał lub zakończy działanie. Kanoniczny wzorzec sprzątania plików tymczasowych obejmuje:
- Zarejestrowanie pułapki natychmiast po utworzeniu pliku tymczasowego.
- Ustawienie pułapki na
EXIT, aby sprzątanie odbywało się zarówno przy normalnym, jak i nieprawidłowym zakończeniu. - Ustawienie również pułapek na
INTiTERM, jeśli skrypt działa długo lub jest interaktywny.
Gwarantuje to brak osieroconych plików, nawet jeśli skrypt zostanie zakończony w trakcie działania.
#!/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."Tworzenie katalogów tymczasowych za pomocą mktemp -d
Czasami potrzebuje Pan/Pani całego katalogu do przechowywania wielu plików — na przykład podczas tworzenia archiwum lub rozpakowywania archiwum tar przed przetworzeniem. Proszę użyć mktemp -d, aby utworzyć bezpieczny katalog tymczasowy.
- Katalog jest tworzony z uprawnieniami
0700(dostęp ma tylko właściciel). - Proszę posprzątać za pomocą
rm -rfw funkcji trap — należy uważać, aby usuwać wyłącznie zmienną, nigdy ścieżkę wpisaną na stałe. - Proszę używać podwójnych cudzysłowów i przed wywołaniem
rm -rfsprawdzić, czy zmienna nie jest pusta. To dodatkowe zabezpieczenie.
#!/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."Problem równoczesnych uruchomień skryptu
Zadania cron, liczniki czasu systemd oraz skrypty uruchamiane ręcznie mogą z łatwością uruchomić jednocześnie wiele instancji tego samego skryptu. Powoduje to:
- Podwójne przetwarzanie: Te same rekordy bazy danych lub pliki są przetwarzane dwukrotnie.
- Uszkodzone dane wyjściowe: Dwie instancje jednocześnie zapisują do tego samego pliku wyjściowego.
- Zakleszczenia lub częściowy stan: Obie instancje modyfikują współdzielone zasoby w przeplatanej, nieprzewidywalnej kolejności.
Tradycyjnym rozwiązaniem było zapisanie pliku PID i sprawdzanie go przy uruchomieniu, ale takie podejście pozostawia okno wyścigu między sprawdzeniem a zapisem. Właściwym współczesnym rozwiązaniem jest flock, który używa mechanizmu blokad doradczych jądra, zapewniając blokadę odporną na wyścigi.
Blokowanie za pomocą flock: wzorzec jednolinijkowy
flock uzyskuje blokadę doradczą na deskryptorze pliku przed uruchomieniem polecenia. Najprostsze użycie polega na opakowaniu całego skryptu w wierszu poleceń:
flock -n /var/lock/myscript.lock bash myscript.sh
-n(tryb nieblokujący): natychmiast kończy działanie ze statusem 1, jeśli blokada jest już zajęta, zamiast czekać.- Bez
-npolecenieflockblokuje działanie do czasu udostępnienia blokady — jest to przydatne podczas kolejkowania. - Sam plik blokady jest tylko znacznikiem; jego zawartość nie ma znaczenia. Można go bezpiecznie pozostawić między uruchomieniami.
- Gdy proces posiadający blokadę kończy działanie, jądro automatycznie ją zwalnia — ręczne czyszczenie nie jest potrzebne.
#!/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 wewnątrz skryptu z użyciem deskryptora pliku
Aby blokować skrypt wewnątrz, zamiast opakowywać go z zewnątrz, proszę użyć exec do otwarcia deskryptora pliku, a następnie wywołać flock dla tego deskryptora. Jest to idiomatyczny wzorzec stosowany w skryptach produkcyjnych.
exec 200>"$LOCKFILE"otwiera plik na deskryptorze 200 do zapisu (tworząc go w razie potrzeby).flock -n 200próbuje zablokować deskryptor 200 w trybie nieblokującym.- Ponieważ blokada jest powiązana z deskryptorem pliku, a nie z nazwą pliku, zostaje automatycznie zwolniona po zakończeniu procesu powłoki.
- Deskryptory o numerach 200–299 są zwyczajowo używane, aby uniknąć konfliktów ze standardowym wejściem, wyjściem i wyjściem błędów.
#!/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."Łączenie mktemp i flock w jednym skrypcie
Rzeczywiście odporne skrypty potrzebują obu mechanizmów: blokady zapobiegającej równoczesnym uruchomieniom oraz bezpiecznych plików tymczasowych na dane pośrednie. Oto kompletny wzorzec łączący obie techniki:
- Najpierw należy uzyskać blokadę — przed utworzeniem jakichkolwiek plików tymczasowych — aby tylko jedna instancja w ogóle wykonywała pracę.
- Po potwierdzeniu uzyskania blokady należy utworzyć zasoby tymczasowe.
- Należy zarejestrować
trapnatychmiast po utworzeniu zasobów tymczasowych, aby czyszczenie było gwarantowane niezależnie od sposobu zakończenia skryptu. - Plik blokady nigdy nie powinien znajdować się w katalogu tymczasowym — musi przetrwać między uruchomieniami, aby
flockmógł się do niego odwołać.
#!/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"Katalogi blokad jako alternatywny mechanizm blokowania
W systemach, w których flock jest niedostępny (na przykład w niektórych systemach wbudowanych lub systemach plików sieciowych, takich jak NFS), można zamiast niego użyć katalogów blokad. W systemach POSIX polecenie mkdir jest atomowe: kończy się powodzeniem tylko wtedy, gdy dany katalog jeszcze nie istnieje.
- Należy utworzyć katalog blokady za pomocą
mkdir /tmp/myscript.lock.d— jeśli inna instancja już go utworzyła,mkdirnatychmiast zakończy się niepowodzeniem. - Wewnątrz katalogu można przechowywać metadane (na przykład PID) na potrzeby diagnostyki.
- Należy zawsze usuwać katalog w funkcji
trapobsługującejEXIT. - Uwaga: W przeciwieństwie do
flockblokada katalogowa NIE jest automatycznie zwalniana, gdy proces zostanie zakończony sygnałem-9lub komputer uruchomi się ponownie — należy dodać sprawdzanie nieaktualnych blokad.
#!/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."Oczekiwanie z limitem czasu za pomocą flock
Czasami należy zaczekać na blokadę zamiast natychmiast kończyć działanie — ale nie czekać w nieskończoność. flock obsługuje limit czasu za pomocą opcji -w.
flock -w 10 200czeka na blokadę maksymalnie 10 sekund, a następnie kończy działanie ze statusem 1, jeśli nadal jest ona niedostępna.- Jest to idealne rozwiązanie dla skryptów, które powinny czekać w kolejce za krótkotrwałym poprzednikiem, ale zrezygnować, jeśli poprzednik utknął.
- Opcję
-wnależy połączyć z użytecznym komunikatem o błędzie zawierającym kontekst — ścieżkę pliku blokady oraz czas oczekiwania — aby operatorzy mogli szybko zdiagnozować zawieszenie.
#!/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 kontrolna zabezpieczeń: bezpieczne zasoby tymczasowe
Przed wdrożeniem dowolnego skryptu używającego plików tymczasowych lub blokowania proszę przejść przez tę listę kontrolną:
- Proszę używać
mktemp, nigdy ścieżek wpisanych na stałe —/tmp/myapp.tmpjest przewidywalna i podatna na wykorzystanie. - Proszę natychmiast zapisać ścieżkę —
TMPFILE=$(mktemp ...)przed wykonaniem jakiegokolwiek innego polecenia. - Proszę zarejestrować
trap cleanup EXITnatychmiast po utworzeniu zasobu — nie na końcu skryptu. - Proszę ujmować wszystkie użycia zmiennych w podwójne cudzysłowy —
rm -f "$TMPFILE", nigdyrm -f $TMPFILE. - Proszę preferować
flockzamiast plików PID — blokada jest zarządzana przez jądro i automatycznie zwalniana po awarii. - Domyślnie proszę używać nieblokującej opcji
-n— ciche blokady blokujące ukrywają problemy z wydajnością. - Plik blokady proszę umieścić poza katalogiem tymczasowym — aby przetrwał działanie funkcji czyszczącej.
- Proszę przetestować działanie czyszczenia — uruchomić skrypt, a następnie wykonać
kill -9w trakcie jego działania; sprawdzić, czy nie pozostały żadne pliki (w przypadku skryptów opartych naflock; blokady katalogowe wymagają dodatkowej ostrożności).
Sprawdzenie wiedzy: działanie opcji flock
Zadanie cron uruchamia się co minutę i przetwarza współdzielony plik. Chce Pan/Pani, aby każde nowe wywołanie natychmiast kończyło działanie z błędem, jeśli poprzednie uruchomienie jest nadal aktywne, bez oczekiwania. Które wywołanie flock poprawnie to implementuje?
Podsumowanie: bezpieczne pliki tymczasowe i katalogi blokad
W tej lekcji poznał(a) Pan/Pani dwa podstawowe narzędzia defensywnego zarządzania zasobami w Bashu:
mktemptworzy nieprzewidywalne pliki tymczasowe z bezpiecznymi uprawnieniami (0600) oraz katalogi (0700), eliminując warunki wyścigu i ataki z użyciem dowiązań symbolicznych, które zagrażają ścieżkom wpisanym na stałe.trap cleanup EXITgwarantuje usunięcie pliku tymczasowego przy każdym zakończeniu — normalnym, wywołanym błędem lub sygnałem — jeśli zostanie zarejestrowane natychmiast po utworzeniu pliku.flockzapewnia wymuszane przez jądro blokowanie doradcze: proszę używać-n, aby natychmiast zakończyć działanie w przypadku konfliktu,-w N, aby czekać przez określony czas, oraz wzorcaexec 200>filedo blokowania wewnątrz skryptu, które jądro automatycznie zwalnia po zakończeniu procesu.- Katalogi blokad (
mkdir) stanowią przenośną alternatywę w środowiskach, w którychflockjest niedostępny, ale wymagają jawnego wykrywania nieaktualnych blokad. - Plik blokady należy zawsze przechowywać poza katalogiem tymczasowym, a każdą zmienną używaną podczas czyszczenia ujmować w podwójne cudzysłowy.
Połączenie mktemp + flock + trap pozwala tworzyć skrypty odporne na równoczesne wywołania, nieprzewidywalne awarie i złośliwe manipulowanie systemem plików.
Często zadawane pytania
Czy lekcja „Bezpieczne pliki tymczasowe i katalogi blokad” jest bezpłatna?
Tak — pełny tekst „Bezpieczne pliki tymczasowe i katalogi blokad” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Linux Command Line & Bash Scripting Mastery, przejdź na CoddyKit PRO. Kurs Linux Command Line & Bash Scripting Mastery zawiera 4 lekcji w sumie.
Co nauczysz się w „Bezpieczne pliki tymczasowe i katalogi blokad”?
Używaj mktemp i flock do tworzenia zasobów tymczasowych odpornych na wyścigi oraz zapobiegania równoczesnemu uruchamianiu skryptów. Ćwiczysz Linux Command Line & Bash Scripting Mastery z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Linux Command Line & Bash Scripting Mastery?
Nie wymagamy żadnego doświadczenia. Linux Command Line & Bash Scripting Mastery w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.
Ile czasu zajmuje lekcja „Bezpieczne pliki tymczasowe i katalogi blokad”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Linux Command Line & Bash Scripting Mastery?
Tak. Każda lekcja Linux Command Line & Bash Scripting Mastery zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Tryb ścisły z set -euo pipefail
- Procedury trap do sprzątania i obsługi sygnałów
- Bezpieczne pliki tymczasowe i katalogi blokad
- Skrypty idempotentne i logika ponawiania z narastającym opóźnieniem