GNU/Linux >> Znalost Linux >  >> Linux

Použití Bash pro automatizaci

Vzhledem k nedávnému množství článků pokrývajících základní aspekty Bash (uvedené na konci tohoto článku) je nevyhnutelné, aby jeden z vašich nových kolegů jeden hodil do cloudu. Jak to jde, další logické kroky jsou:

  1. Ověřte, že něco absolutně kritického závisí na bezchybném fungování „cloudového“ skriptu.
  2. Ověřte původního autora scénáře, který úplně zapomněl, jak to ve skutečnosti funguje.
  3. Ověřte, že nejnovější správce má za úkol jej zásadně změnit bez jakéhokoli ověřování.

V tomto článku pomáhám všem administrátorům a pomohu vám vyhnout se všem výše uvedeným chybám. Výsledek pak vede ke šťastnějšímu řízení a doufejme, že i k naší pokračující zaměstnanosti.

Jak se píše "Bash skript"

Abyste se stali osvícenými (a pro vaši lásku k $DEITY), zkontrolujte svůj kód v nástroji pro správu zdrojového kódu (SCM). I když se učíte, používejte jako hřiště místní úložiště. Nejen, že vám tato praxe umožní zaznamenat vaše úsilí v průběhu času, ale také vám umožní snadno napravit chyby. Existuje mnoho skvělých článků o tom, jak začít s git které vřele doporučuji.

Rychlá poznámka o používání a učení Bash, protože tento skriptovací jazyk přináší jedinečnou sadu -ismů a stylistických preferencí autora:Jakmile objevíte něco, co vám připadá nové (syntaxe, styl nebo jazykový konstrukt), okamžitě si to vyhledejte. . Věnujte svůj čas porozumění nové položce z manuálové stránky (první volba) nebo Advanced Bash Scripting Guide (obojí jsou dostupné offline). Zpočátku vás to zpomalí, ale postupem času vám tato praxe pomůže vybudovat si znalosti o tom, kde hledat odpovědi.

Zápis opakovaně použitelných nugetů Bash jako knihoven

Skripty v automatizaci se nejlépe píší, když zahrnují filozofii Unixu:Mnoho malých nástrojů, které dělají pouze jednu věc. To znamená, že budete mnohem lépe psát malé, specializované skripty a knihovny, než s jedním obřím „kuchyňským dřezem“. I když je pravda, že jsem napsal a udržoval několik příšer (občas poslouží účelu).

Automatizační skripty často musí být srozumitelné a udržovatelné více než jedním autorem. S mnoha malými skripty, které poletují (a jsou sledovány ve správě verzí), rychle zjistíte, že potřebujete sdílet odkaz na hodnoty pro názvy, verze, cesty nebo adresy URL. Zápis těchto společných prvků do knihoven také poskytuje další mentální prostor pro správce, aby ocenili inline dokumentaci. Navíc se díky tomu stává využití jednotkových testů téměř triviální (k tomuto tématu se dostaneme na konci).

Pojďme si od začátku procvičit dobrou hygienu kódu vytvořením místního úložiště „play“. Ve svém novém úložišti vytvořte místo, které bude obsahovat naše skripty a soubory knihovny. Rád se držím dobře srozumitelných standardů FHS pro jednoduchost. Vytvořte adresáře ./bin/ a ./lib/ v kořenovém adresáři úložiště. Ve větším automatizačním projektu bych stále používal tato jména, ale mohou být hluboce pohřbeny (řekněme pod scripts nebo tools podadresář).

Povídání o cestách mě přivádí ke skvělému tématu pro první knihovnu. Potřebujeme způsob, jak naše budoucí komponenty odkazovat na konstrukční prvky a vysoké hodnoty. Pomocí svého oblíbeného editoru vytvořte soubor ./lib/anchors.sh a přidejte obsah níže:



# A Library of fundamental values
# Intended for use by other scripts, not to be executed directly.

# Set non-'false' by nearly every CI system in existence.
CI="${CI:-false}"  # true: _unlikely_ human-presence at the controls.
[[ $CI == "false" ]] || CI='true'  # Err on the side of automation

# Absolute realpath anchors for important directory tree roots.
LIB_PATH=$(realpath $(dirname "${BASH_SOURCE[0]}"))
REPO_PATH=$(realpath "$LIB_PATH/../")  # Specific to THIS repository
SCRIPT_PATH=$(realpath "$(dirname $0)")

Soubor začíná dvěma prázdnými řádky a první komentář vysvětluje proč. Knihovna by neměla být nastaven jako spustitelný (navzdory názvu končícím na .sh, což naznačuje jeho typ). Pokud byla knihovna spuštěna přímo, je možné, že by to mohlo způsobit zmizení uživatelského shellu (nebo hůře). Zakázání přímého spouštění (chmod -x ./lib/anchors.sh ) je první úrovní ochrany správce pro začátečníky. Komentář na začátku souboru je druhá úroveň.

Podle konvence by komentáře měly vždy popisovat proč (nikoli co ) z následujících prohlášení. Čtenář si může jednoduše přečíst prohlášení, aby pochopil, co dělá, ale nemůže spolehlivě vytušit, co si autor v tu chvíli myslel. Než však půjdu hlouběji, musím podrobně popsat problém, který lidi s Bash často zaskočí.

Výchozí hodnoty Bash poskytují prázdný řetězec při odkazu na nedefinovanou proměnnou. CI proměnná (nebo něco podobného ve vaší automatizaci) je určena k označení pravděpodobné nepřítomnosti člověka. Bohužel pro vládce robotů budou lidé pravděpodobně muset spustit skript ručně alespoň jednou. V tuto chvíli je pravděpodobné, že zapomenou nastavit hodnotu pro CI před stisknutím klávesy Enter.

Musíme tedy nastavit výchozí hodnotu pro hodnotu a zajistit, aby byla vždy buď pravda, nebo nepravda. Výše uvedený příklad kódu knihovny ukazuje, jak otestovat, zda je řetězec prázdný, a jak vynutit, aby řetězec obsahoval jednu z páru hodnot. Tak, jak jsem četl první skupinu příkazů v anchors.sh je:

Definujte 'CI ' dále jako výsledek:

  1. Zkoumání předchozí hodnoty CI (může být nedefinovaný, takže prázdný řetězec).
  2. ':- část znamená:
    1. Pokud byl hodnotou prázdný řetězec, představuje řetězec 'false ' místo toho.
    2. Pokud hodnota nebyl prázdný řetězec, použijte cokoliv, co to bylo (včetně 'DaRtH VaDeR ').

Otestujte věc uvnitř '[[ ' a ' ]] ':

  1. Pokud je nová hodnota 'CI ' se rovná doslovnému řetězci "false ", hodí ukončovací kód 0 (což znamená úspěch nebo pravda).
  2. V opačném případě použijte návratový kód 1 (což znamená selhání nebo nepravda)

Pokud test skončil s 0 , pokračujte dalším řádkem nebo (' || ' část), předpokládejme, že začínající administrátor nastaví CI=YES!PLEASE nebo sada dokonalého stroje CI=true . Okamžitě nastavte hodnotu 'CI ' na doslovný řetězec 'true '; protože dokonalé stroje jsou lepší, nedělejte chyby.

Pro zbytek této knihovny jsou hodnoty kotevní cesty téměř vždy užitečné ve skriptech spouštěných v automatizaci z úložiště. Při použití ve větším projektu byste museli upravit relativní umístění adresáře knihovny s ohledem na kořen úložiště. V opačném případě nechám porozumění těmto tvrzením jako výzkumné cvičení pro čtenáře (udělejte to nyní, pomůže vám to později).

Použití knihoven Bash ve skriptech Bash

K načtení knihovny použijte source vestavěný příkaz. Tento příkaz není přepychový. Zadejte mu umístění souboru a ono pak načte a spustí obsah přímo tam a tam, což znamená, že kontext kódu knihovny za běhu bude ve skutečnosti skript, který source d it.

Abyste zabránili tomu, aby vám z uší odkapávalo příliš mnoho mozku, zde je jednoduchý ./bin/example.sh skript pro ilustraci:

#!/bin/bash

LIB_PATH="$PWD/$(dirname $0)/../lib/anchors.sh"
echo "Before loading: $LIB_PATH"
set -ax
cd /var/tmp
source $LIB_PATH
echo "After loading: $(export -p | grep ' LIB_PATH=')"

Okamžitě si můžete všimnout, že skript před načtením knihovny změnil běhový kontext. Také definuje LIB_PATH lokálně a ukazuje jej na soubor (zmateně, místo na adresář) s relativní cestou (pro ilustrativní účely).

Pokračujte a spusťte tento skript a prozkoumejte výstup. Všimněte si, že všechny operace v knihovně anchors.sh běžel uvnitř /var/tmp/ adresář a automaticky exportoval jeho definice. Stará definice pro LIB_PATH byl clobbered a exportován a v set -ax . Tato skutečnost je viditelná ve výstupu z declare -x pocházející z export příkaz. Doufejme, že výstup ladění (x v set -ax ) je pochopitelné.

Při takovém ladění Bash vytiskne všechny mezilehlé hodnoty při analýze každého řádku. Tento skript jsem sem zahrnul, abych ukázal, proč byste nikdy nechtěli set -ax nebo změnit adresáře pomocí příkazů z nejvyšší úrovně knihovny. Pamatujte, že instrukce knihovny jsou ve skriptu vyhodnocovány při načítání. Takže změna prostředí v knihovně způsobí, že v jakémkoli skriptu použitém source budou existovat vedlejší efekty za běhu načíst to. Vedlejší účinky, jako je tento, zaručeně přivedou alespoň jednoho správce systému k úplnému šílenství. Nikdy nevíte, tím adminem bych mohl být já, tak to nedělejte.

Jako praktický příklad zvažte imaginární knihovnu, která definuje funkci pomocí proměnné prostředí uživatelské jméno/heslo pro přístup ke vzdálené službě. Pokud knihovna provedla nejvyšší úroveň set -ax před touto funkcí, pak pokaždé, když je načtena, výstup ladění bude zahrnovat zobrazení těchto proměnných, rozstřikování vašich tajemství všude, aby je každý viděl. Horší je, že pro začínajícího kolegu bude obtížné (z pohledu volajícího skriptu) zakázat výstup, aniž by křičel na klávesnici.

Na závěr je důležité si uvědomit, že knihovny se „dějí“ v kontextu jejich volajícího. Tento faktor je také důvodem příkladu anchors.sh můžete použít  $0   (cesta ke spustitelnému skriptu a název souboru), ale cesta ke knihovně samotné je dostupná pouze prostřednictvím "magického" '${BASH_SOURCE[0]}' (prvek pole). Tento faktor může být zpočátku matoucí, ale měli byste se snažit zůstat disciplinovaní. Vyhněte se širokým, dalekosáhlým příkazům v knihovnách. Když to uděláte, všichni noví administrátoři budou trvat na zaplacení vašich koblih.

Psaní jednotkových testů pro knihovny

Psaní jednotkových testů se může zdát jako skličující úkol, dokud si neuvědomíte, že dokonalé pokrytí je obvykle ztráta času. Je však dobrým zvykem vždy používat a aktualizovat svůj testovací kód, když se dotknete kódu knihovny. Cílem psaní testu je postarat se o nejběžnější a nejzřejmější případy použití a poté pokračovat. Nevěnujte velkou pozornost rohovým pouzdrům nebo méně než běžnému použití. Navrhuji také zpočátku zaměřit své úsilí na testování knihoven na úroveň testování jednotek namísto testování integrace.

Podívejme se na další příklad:spustitelný skript ./lib/test-anchors.sh :

#!/bin/bash

# Unit-tests for library script in the current directory
# Also verifies test script is derived from library filename

TEST_FILENAME=$(basename $0)  # prefix-replace needs this in a variable
SUBJ_FILENAME="${TEST_FILENAME#test-}"; unset TEST_FILENAME
TEST_DIR=$(dirname $0)/

ANY_FAILED=0

# Print text after executing command, set ANY_FAILED non-zero on failure
# usage: test_cmd "description" <command> [arg...]

test_cmd() {
   local text="${1:-no test text given}"
   shift
   if ! "$@"; then
      echo "fail - $text"; ANY_FAILED=1;
   else
      echo "pass - $text"
   fi
}

test_paths() {
   source $TEST_DIR/$SUBJ_FILENAME
   test_cmd "Library $SUBJ_FILENAME is not executable" \
      test ! -x "$SCRIPT_PATH/$SUBJ_FILENAME"
   test_cmd "Unit-test and library in same directory" \
      test "$LIB_PATH" == "$SCRIPT_PATH"
   for path_var in LIB_PATH REPO_PATH SCRIPT_PATH; do
      test_cmd "\$$path_var is defined and non-empty: ${!path_var}" \
         test -n "${!path_var}"
      test_cmd "\$$path_var referrs to existing directory" \
         test -d "${!path_var}"
   done
}

# CI must only/always be either 'true' or 'false'.
# Usage: test_ci <initial value> <expected value>

test_ci() {
   local prev_CI="$CI"  # original value restored at the end
   CI="$1"
   source $TEST_DIR/$SUBJ_FILENAME
   test_cmd "Library $SUBJ_FILENAME loaded from $TEST_DIR" \
      test "$?" -eq 0
   test_cmd "\$CI='$1' becomes 'true' or 'false'" \
      test "$CI" = "true" -o "$CI" = "false"
   test_cmd "\$CI value '$2' was expected" \
      test "$CI" = "$2"
   CI="$prev_CI"
}

test_paths
test_ci "" "false"
test_ci "$RANDOM" "true"
test_ci "FoObAr" "true"
test_ci "false" "false"
test_ci "true" "true"

# Always run all tests and report, exit non-zero if any failed

test_cmd "All tests passed" \
   test "$ANY_FAILED" -eq 0
[[ "$CI" == "false" ]] || exit $ANY_FAILED  # useful to automation
exit(0)

Důvod, proč jsem vložil tento skript do ./lib (na rozdíl od ./bin ) je jednak z pohodlnosti a jednak proto, že testy se nikdy nesmí spoléhat na použití kódu, který kontrolují. Protože tento test potřebuje zkontrolovat cesty, je snazší jej umístit do stejné cesty jako knihovnu. Jinak je tento přístup věcí osobních preferencí. Neváhejte a spusťte test hned, protože vám může pomoci porozumět kódu.

Zabalení

Tento článek v žádném případě nepředstavuje úplné využití Bash v automatizaci. Snažil jsem se vám však vštípit základní znalosti a doporučení, které vám (pokud je budete dodržovat) nepochybně usnadní život. Pak, i když se věci stanou obtížné, bude užitečné pevné pochopení důležitosti běhového kontextu.

A konečně, skriptování v automatizaci nebo pro automatizaci může být neúprosné k chybám. Zavedení i základních jednotkových testů pro vaše knihovny zvýší sebevědomí a pomůže další osobě, která přijde (což byste po pěti letech zapomínání mohli být vy). Všechny ukázkové kódy použité v tomto článku najdete online zde.

Máte zájem oprášit své základy Bash? Podívejte se:

  • Jak programovat pomocí Bash:Logické operátory a rozšíření shellu
  • 5 způsobů, jak zlepšit skripty Bash
  • Začínáme se skriptováním shellu
  • 10 základních příkazů Linuxu, které potřebujete znát
  • Tipy a triky proměnných prostředí Linux

[ Chcete si vyzkoušet Red Hat Enterprise Linux? Stáhněte si ji nyní zdarma. ]


Linux
  1. Zpracování chyb ve skriptech Bash

  2. Použití nástroje SS pro řešení problémů se sítí

  3. Použít rozšíření .sh nebo .bash pro skripty Bash?

  1. Použití Linux Sleep Command ve skriptech Bash

  2. Použití příkazu Linux Basename ve skriptech Bash

  3. Bash skript:Použití příkazu script z bash skriptu pro protokolování relace

  1. Bash For Loop

  2. Psaní komentářů ve skriptech Bash

  3. automatizace relace telnet pomocí bash skriptů