0Pricing
Linux Command Line & Bash Scripting Mastery · Lekcja

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 znakami X.
  • Każdy znak X jest 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 INT i TERM, 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 -rf w 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 -rf sprawdzić, 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 -n polecenie flock blokuje 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
fi

flock 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 200 pró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ć trap natychmiast 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 flock mó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, mkdir natychmiast 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 trap obsługującej EXIT.
  • Uwaga: W przeciwieństwie do flock blokada katalogowa NIE jest automatycznie zwalniana, gdy proces zostanie zakończony sygnałem -9 lub 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 200 czeka 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ę -w należ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.tmp jest przewidywalna i podatna na wykorzystanie.
  • Proszę natychmiast zapisać ścieżkę — TMPFILE=$(mktemp ...) przed wykonaniem jakiegokolwiek innego polecenia.
  • Proszę zarejestrować trap cleanup EXIT natychmiast po utworzeniu zasobu — nie na końcu skryptu.
  • Proszę ujmować wszystkie użycia zmiennych w podwójne cudzysłowy — rm -f "$TMPFILE", nigdy rm -f $TMPFILE.
  • Proszę preferować flock zamiast 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 -9 w trakcie jego działania; sprawdzić, czy nie pozostały żadne pliki (w przypadku skryptów opartych na flock; 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:

  • mktemp tworzy 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 EXIT gwarantuje 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.
  • flock zapewnia 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 wzorca exec 200>file do 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órych flock jest 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

  1. Tryb ścisły z set -euo pipefail
  2. Procedury trap do sprzątania i obsługi sygnałów
  3. Bezpieczne pliki tymczasowe i katalogi blokad
  4. Skrypty idempotentne i logika ponawiania z narastającym opóźnieniem
← Powrót do Linux Command Line & Bash Scripting Mastery