0Pricing
Linux Command Line & Bash Scripting Mastery · Lekcja

Tryb ścisły z set -euo pipefail

Włącz szybkie przerywanie po błędzie i dokładnie zrozum, które błędy każda flaga trybu ścisłego wykrywa, a które pomija.

Tryb ścisły z set -euo pipefail to bezpłatna lekcja Linux Command Line & Bash Scripting Mastery na CoddyKit. To lekcja 1 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 Bash domyślnie po cichu ignoruje błędy

Domyślnie Bash działa dalej, nawet gdy polecenia kończą się błędem. Prowadzi to do subtelnych i trudnych do zdiagnozowania awarii w skryptach produkcyjnych.

Rozważmy skrypt, który próbuje utworzyć kopię zapasową:

  • Literówka w ścieżce powoduje niepowodzenie polecenia cp
  • Bash ignoruje błąd i kontynuuje działanie
  • Skrypt zgłasza powodzenie, mimo że dane nigdy nie zostały zarchiwizowane

To problem cichych błędów. Tryb restrykcyjny go rozwiązuje, sprawiając, że Bash zachowuje się jak język kompilowany: natychmiast zatrzymuje się, gdy coś pójdzie nie tak.

#!/usr/bin/env bash
# Without strict mode — dangerous default behavior

cp /important/data /backups/data   # fails (path doesn't exist)
echo "Backup complete"              # still prints — false confidence!
rm -rf /tmp/staging                # still runs — potentially destructive

Trzy podstawowe flagi: set -euo pipefail

Tryb restrykcyjny włącza się, umieszczając następujący wiersz na początku każdego skryptu:

set -euo pipefail

Włącza to trzy odrębne zabezpieczenia:

  • -e — natychmiast kończy działanie, jeśli dowolne polecenie zwróci status różny od zera
  • -u — traktuje niezdefiniowane zmienne jako błąd (zamiast rozwijać je do pustego ciągu)
  • -o pipefail — potok kończy się niepowodzeniem, jeśli zawiedzie dowolne zawarte w nim polecenie, a nie tylko ostatnie

Razem tworzą standardowy nagłówek ochronny poważnych skryptów Bash. Każda flaga wykrywa inną klasę błędów.

#!/usr/bin/env bash
set -euo pipefail

echo "Strict mode is now active"
echo "Every command failure will abort the script"

Zrozumienie set -e (errexit)

set -e (zapisywane również jako set -o errexit) powoduje natychmiastowe zakończenie skryptu, gdy polecenie zakończy się statusem różnym od zera.

Najważniejsze zachowania:

  • Proste polecenia: false, grep pattern file (brak dopasowania), ls /nonexistent — wszystkie powodują zakończenie
  • Kod wyjścia ostatniego polecenia w skrypcie staje się kodem wyjścia skryptu
  • Polecenia w warunkach if są wyłączone z tej zasady — -e nie działa dla wyrażenia testowego
  • Polecenia zakończone przez || true również są wyłączone z tej zasady (zobacz następne sceny)

Proszę traktować -e jako pierwszą linię obrony przed cichym kontynuowaniem działania po błędzie.

#!/usr/bin/env bash
set -e

echo "Before failure"
ls /this/path/does/not/exist   # exits here with code 2
echo "This line never runs"

Zrozumienie set -u (nounset)

set -u (zapisywane również jako set -o nounset) sprawia, że Bash traktuje każde odwołanie do niezdefiniowanej zmiennej jako błąd krytyczny.

Bez -u literówka, taka jak $FLENAME zamiast $FILENAME, zostaje po cichu rozwinięta do pustego ciągu, przez co polecenia działają nieoczekiwanie — a nawet niebezpiecznie (proszę wyobrazić sobie rm -rf "$TMPDIR/", gdy $TMPDIR nie jest zdefiniowana).

Ważne wyjątki:

  • ${VAR:-default} — bezpieczne podstawienie wartości domyślnej, nie powoduje zadziałania -u
  • ${VAR:+value} — rozwinięcie warunkowe, również bezpieczne
  • "$@" i "$*" są wyłączone z tej zasady, gdy nie przekazano argumentów pozycyjnych
#!/usr/bin/env bash
set -euo pipefail

# Safe: provide a default for optional vars
OUTPUT_DIR="${1:-/tmp/output}"
LOG_LEVEL="${LOG_LEVEL:-info}"

echo "Writing to: $OUTPUT_DIR"
echo "Log level: $LOG_LEVEL"

# This would abort the script:
# echo "$UNDEFINED_VAR"   # bash: UNDEFINED_VAR: unbound variable

Zrozumienie -o pipefail

Bez pipefail status wyjścia potoku zależy wyłącznie od ostatniego polecenia. Wcześniejsze błędy są po cichu ignorowane.

Przykład bez pipefail:

  • cat /missing/file | wc -l
  • cat kończy się kodem wyjścia 1, ale wc -l kończy się powodzeniem z kodem 0
  • Potok zwraca 0 — powodzenie! Mimo że dane zostały utracone.

Po włączeniu pipefail Bash zwraca kod wyjścia skrajnego prawego polecenia, które zakończyło się błędem. Dzięki temu błędy potoków stają się widoczne i możliwe do przechwycenia.

Uwaga: pipefail nie jest flagą literową — należy ją ustawić za pomocą -o pipefail.

#!/usr/bin/env bash
set -euo pipefail

# With pipefail: this aborts if grep finds nothing (exit 1)
# grep returns 1 when no match found
ps aux | grep "[n]ginx" | awk '{print $2}'

echo "If we reach here, nginx is running"

Zamierzone błędy poleceń — używanie || true

Czasami poleceniu wolno zakończyć się błędem. Przy włączonym set -e trzeba jawnie określić, że dany błąd jest akceptowalny; w przeciwnym razie skrypt zostanie przerwany.

Idiomatycznym rozwiązaniem jest || true, które dodaje zawsze kończące się powodzeniem działanie awaryjne:

  • command || true — całkowicie ignoruje błąd
  • command || echo "Warning: step failed, continuing" — rejestruje ostrzeżenie i kontynuuje działanie
  • command || { echo "fatal"; exit 1; } — niestandardowa obsługa błędu

Ten wzorzec jasno wyraża intencję w kodzie: zwykłe polecenie oznacza „to musi się udać”, a || true oznacza „to może się nie udać i jest to akceptowalne”.

#!/usr/bin/env bash
set -euo pipefail

# Remove temp dir if it exists — OK if it doesn't
rm -rf /tmp/my_workspace || true
mkdir -p /tmp/my_workspace

# Check if a service is running — OK if not
if systemctl is-active --quiet nginx 2>/dev/null || true; then
  echo "nginx is active"
fi

# Grep that may find nothing — OK
grep 'ERROR' /var/log/app.log || true

echo "Done"

Czego set -e NIE przechwytuje

set -e ma dobrze znane wyjątki i pułapki. Ich zrozumienie pozwala uniknąć fałszywego poczucia bezpieczeństwa:

  • Polecenia w warunkach if / while / until — wyrażenie testowe jest domyślnie wyłączone z tej zasady
  • Polecenia negowane za pomocą ! — ! false nie powoduje zakończenia
  • Ostatnie polecenie przed || — na przykład false || handle_error
  • Status wyjścia podpowłoki w określonych kontekstach — na przykład VAR=$(failing_command) w niektórych wersjach Bash
  • Wartości zwracane przez funkcje — liczy się tylko ostatnie polecenie w funkcji

Tryb restrykcyjny nie zastępuje jawnego sprawdzania błędów — jest siatką bezpieczeństwa, która wychwytuje większość przypadkowych błędów.

#!/usr/bin/env bash
set -euo pipefail

# These do NOT trigger -e:
if false; then echo "never"; fi         # -e exempt in conditions
! false                                  # negation exempts
false || echo "handled"                 # || exempts the left side

# This DOES trigger -e (no condition, no ||):
# false

echo "Script continues after exempted failures"

Podpowłoki i funkcje w trybie restrykcyjnym

Ustawienia trybu restrykcyjnego są dziedziczone przez podpowłoki, ale w funkcjach i podstawieniach poleceń zachowują się w subtelny sposób.

Najważniejsze zasady:

  • Funkcje dziedziczą -e, -u i pipefail z wywołującej powłoki
  • Niezerowa wartość zwracana przez funkcję powoduje zakończenie powłoki wywołującej (gdy ustawiono -e) — chyba że wywołanie znajduje się w warunku lub po ||
  • Podstawienie polecenia $(): w starszych wersjach Bash polecenie kończące się błędem wewnątrz $() może nie uruchomić -e w powłoce nadrzędnej; dla bezpieczeństwa należy przypisać wynik, a następnie użyć go osobno
  • Jawne podpowłoki () dziedziczą wszystkie flagi
#!/usr/bin/env bash
set -euo pipefail

setup_workspace() {
  local dir="$1"
  mkdir -p "$dir"          # fails here if permissions denied
  cd "$dir"
  echo "Ready in $(pwd)"
}

# Safe pattern: assign result then use it
TODAY=$(date +%Y-%m-%d)   # capture separately
WORKDIR="/tmp/run_${TODAY}"

setup_workspace "$WORKDIR"
echo "Workspace: $WORKDIR"

Łączenie trybu restrykcyjnego z przechwytywaniem błędów

Tryb restrykcyjny mówi Bashowi, kiedy ma się zatrzymać. trap dla ERR umożliwia wykonanie czyszczenia lub diagnostyki przed zakończeniem skryptu.

Typowy wzorzec wygląda następująco:

  • Ustawienie trybu restrykcyjnego na początku
  • Zdefiniowanie funkcji cleanup lub on_error
  • Zarejestrowanie jej za pomocą trap 'on_error' ERR
  • Opcjonalne przechwycenie również EXIT w celu zagwarantowania czyszczenia niezależnie od powodzenia lub błędu

Ważne: należy użyć set -E (wielkie E, nazywane również errtrace), aby pułapka ERR była dziedziczona także przez funkcje i podpowłoki — bez tego pułapki działają tylko w głównym ciele powłoki.

#!/usr/bin/env bash
set -Eeuo pipefail

on_error() {
  local exit_code=$?
  local line_number=${BASH_LINENO[0]}
  echo "ERROR: command failed with code ${exit_code} at line ${line_number}" >&2
}

cleanup() {
  echo "Cleaning up temporary files..." >&2
  rm -rf /tmp/my_run_dir 2>/dev/null || true
}

trap on_error ERR
trap cleanup EXIT

mkdir -p /tmp/my_run_dir
echo "hello" > /tmp/my_run_dir/output.txt
cat /tmp/my_run_dir/output.txt
echo "Done"

Lokalne wyłączanie trybu restrykcyjnego

Czasami blok kodu jest celowo „nieuporządkowany” — na przykład podczas sprawdzania dostępności opcjonalnych narzędzi lub uruchamiania starszych poleceń, które zwracają wartość różną od zera z powodów niezwiązanych z błędem. Można tymczasowo wyłączyć tryb restrykcyjny, a następnie go przywrócić.

Bezpieczny wzorzec:

  • Należy zapisać stan za pomocą set +e (wyłącza -e), uruchomić blok, a następnie ponownie włączyć tryb za pomocą set -e
  • Można też użyć podpowłoki ( set +e; ... ), aby flaga powłoki nadrzędnej nigdy nie została zmieniona
  • Flagi należy zawsze włączyć ponownie natychmiast po zakończeniu ryzykownego bloku — pozostawienie ich wyłączonych jest częstym źródłem błędów

Jeśli blok obejmuje wiele poleceń, należy preferować formę z podpowłoką, ponieważ automatycznie przywraca ona flagi przy zakończeniu.

#!/usr/bin/env bash
set -euo pipefail

# Probe for optional tools without aborting
HAS_JQ=false
(
  set +e
  command -v jq > /dev/null 2>&1
  [[ $? -eq 0 ]] && echo "jq_found"
) && HAS_JQ=true || true

if [[ "$HAS_JQ" == "true" ]]; then
  echo "jq is available — using JSON output"
else
  echo "jq not found — using plain text"
fi

Kompletny szablon skryptu w trybie restrykcyjnym

Oto gotowy do użycia w środowisku produkcyjnym szablon, który łączy wszystkie omówione w tej lekcji dobre praktyki trybu restrykcyjnego:

  • set -Eeuo pipefail — wszystkie cztery flagi, w tym errtrace
  • IFS=$'\n\t' — bezpieczniejsze dzielenie słów (zapobiega dzieleniu po spacjach)
  • Pułapki ERR + EXIT na potrzeby diagnostyki i czyszczenia
  • Jawne wartości domyślne dla opcjonalnych parametrów
  • readonly i local do ograniczania zakresu zmiennych

Proszę kopiować ten szablon na początku każdego nietrywialnego skryptu Bash, aby od razu skorzystać z działania zgodnego z zasadą „zatrzymaj się przy pierwszym błędzie” oraz z możliwych do prześledzenia błędów.

#!/usr/bin/env bash
set -Eeuo pipefail
IFS=$'\n\t'

# ── Constants ────────────────────────────────────────────
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SCRIPT_NAME="$(basename "$0")"

# ── Trap handlers ────────────────────────────────────────
err_handler() {
  echo "[${SCRIPT_NAME}] ERROR on line ${BASH_LINENO[0]}: exit ${?}" >&2
}
cleanup() {
  echo "[${SCRIPT_NAME}] Exiting" >&2
}
trap err_handler ERR
trap cleanup EXIT

# ── Defaults ─────────────────────────────────────────────
ENV="${1:-production}"
MAX_RETRIES="${MAX_RETRIES:-3}"

# ── Main ─────────────────────────────────────────────────
main() {
  echo "Running in env=${ENV}, max_retries=${MAX_RETRIES}"
  echo "Script dir: ${SCRIPT_DIR}"
}

main "$@"

Sprawdzenie wiedzy: działanie pipefail

Sprawdź, czy rozumiesz, jak pipefail wpływa na kody wyjścia potoków.

Podsumowanie: tryb ścisły z set -euo pipefail

W tej lekcji dowiedział się Pan / dowiedziała się Pani, jak sprawić, aby skrypty Bash szybko i wyraźnie zgłaszały błędy dzięki trybowi ścisłemu.

Trzy flagi i zakres ich działania:

  • -e (errexit) — kończy działanie po napotkaniu polecenia o niezerowym statusie; nie obowiązuje w warunkach ani po ||
  • -u (nounset) — przerywa działanie przy odwołaniu do niezdefiniowanej zmiennej; dla opcjonalnych zmiennych należy używać ${VAR:-default}
  • -o pipefail — powoduje, że cały potok kończy się błędem, jeśli zawiedzie dowolny jego etap, a nie tylko ostatni

Uzupełniające praktyki:

  • Należy dodać -E (errtrace), aby pułapki ERR były propagowane do funkcji
  • Należy używać trap dla ERR i EXIT do diagnostyki i sprzątania
  • Należy używać || true, aby celowo ignorować błędy
  • W podpowłokach można tymczasowo wyłączyć tę opcję za pomocą set +e na potrzeby starszego kodu lub kodu testującego

Tryb ścisły nie jest cudownym rozwiązaniem — należy znać jego wyjątki — ale jest zdecydowanie najskuteczniejszym nawykiem podczas pisania niezawodnych, odpornych na błędy skryptów Bash.

Często zadawane pytania

Czy lekcja „Tryb ścisły z set -euo pipefail” jest bezpłatna?

Tak — pełny tekst „Tryb ścisły z set -euo pipefail” 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 „Tryb ścisły z set -euo pipefail”?

Włącz szybkie przerywanie po błędzie i dokładnie zrozum, które błędy każda flaga trybu ścisłego wykrywa, a które pomija. Ć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 1 z 4.

Ile czasu zajmuje lekcja „Tryb ścisły z set -euo pipefail”?

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