Skriptovanie v shelli: Premenné

Úvod

Premenné v shellscriptingu sú zábava a keby sme ich nemali, ďaleko by sme sa nedoprogramovali. (Teraz nechajme bokom iné paradigmy programovania.) Nie je na nich nič zložité, ale žiaľ, skrýva sa pri nich množstvo cestičiek, ktoré vás zavedú do močiarov a nepevnej pôdy.

S týmto návodom sa to nestane.

Zabudované premenné

POSIXový shell dáva k dispozícii kopu zabudovaných premenných — ak chcete, môžete ich zoznam obdivovať v špecifikácii.

Ak chceme zistiť hodnotu premennej, stačí uviesť jej názov (HOME) uvedený dolárom. Vytlačme si napr. cestu k domovskému adresáru:

echo $HOME

Výsledok bude:

/home/novotnyr

Expanzia parametrov

Čo sa v skutočnosti stalo? Shell vykonal expanziu parametrov (parameter expansion), do čoho spadá aj expanzia premenných. Predtým, než vykonal príkaz, nahradil všetky dolárové výskyty ich hodnotami.

Keďže HOME je zabudovaná premenná s hodnotou /home/novotnyr/, príkaz echo $HOME sa expanduje na

echo /home/novotnyr

a následne sa vykoná.

Zoznam definovaných premenných

Zoznam všetkých definovaných premenných zistíme cez set.

set

Definícia vlastných premenných

Vlastnú premennú definujeme jednoducho:

MENO=Robert

Názov premennej je tradične uvádzaný veľkými písmenami. Dajte si však veľký pozor na túto konštrukciu!

  1. Pri zapisovaní do premennej nepoužívame dolár!
  2. Okolo znaku = nesmú byť medzery!

Pri zapisovaní do premennej nepoužívame dolár!

Ak porušíte zásadu č. 1, uvidíte toto:

novotnyr@itat:~$ $MENO=Robert
-bash: =Robert: command not found

Dolár pred premennou znamená „čítanie jej obsahu”, presnejšie povedané, shell vykoná expanziu premennej HOME. Tá je nedefinovaná a teda expanduje na „nič”, čiže prázdny reťazec

Výsledkom bude teda:

=Robert

čo je očividný nezmysel.

Okolo znaku = nesmú byť medzery!

Ak porušíte druhú zásadu, uvidíte toto:

novotnyr@s:~$ MENO = Robert
-bash: MENO: command not found

Úbohý shell totiž bude chcieť vykonať príkaz MENO s dvoma parametrami = a Robert, čo je samozrejme hlúposť. (Spomínal som močiare?)

Neprepadajte záchvatu estetického formátovania ako v Jave, tu to jednoducho nefunguje.

Definícia premenných s medzerami

Ak chceme premennú s medzerami, je to jednoduché: stačí to uzavrieť do úvodzoviek:

PLNE_MENO="Robert Novotny"

Následne je to prosté:

echo $PLNE_MENO

Exportovanie premenných

Mnoho aplikácií sa spolieha na definíciu premennej prostredia (environment variable), povestné sú niektoré aplikácie v Jave. Ak nastavíte (napr.)

JAVA_HOME=/usr/lib/jvm/java-6-openjdk

tak samotná aplikácia túto premennú neuvidí. Musíte ju totiž exportovať, teda povýšiť shellovskú premennú na premennú prostredia.

export JAVA_HOME

(Znalci Windowsu vedia, že tam je to jednoduchšie: definícia premennej ju zároveň exportne.)

Expanzia parametrov a premenných

Expanzia parametrov umožňuje zjednodušovanie zápisu:

novotnyr@s:~$ echo Ahoj, $PLNE_MENO
Ahoj, Robert Novotny

V tomto prípade sa echo zavolá s troma parametrami: Ahoj,, Robert a Novotny.

Expanzia v úvodzovkách

V duchu zachovania súdnosti sa často reťazce uvádzajú do úvodzoviek:

echo "Ahoj, $PLNE_MENO"

Napodiv, to bude fungovať, pretože expanzia premenných prebieha i vo vnútri úvodzoviek. (Túto vlastnosť vykradlo mnoho skriptovacích jazykov, napr. PHP či Groovy.)

Ak by malo dôjsť k nejednoznačnosti konca premennej, je lepšie uviesť jej názov do kučeravých zátvoriek. Chcete vypísať novotnyr2012? Takto určite nie:

POUZIVATEL=novotnyr
echo "$POUZIVATEL2012.dat"

Výsledkom je totiž:

.dat

Shell sa pokúsi expandovať premennú POUZIVATEL2012, ktorá nie je definovaná. Správna verzia je

echo "${POUZIVATEL}2012.dat"

Ak si chcete zachovať zdravý rozum, je viac než dobré pri čítaní obsahu premenných využiť ich uzavretie do úvodzoviek a to aj v prípade, že to nie je nutné. Ukážok bude viacero.

Expanzia v apostrofoch (…nefunguje)

Ak použijeme reťazec v apostrofoch, zabránime akejkoľvek expanzii:

novotnyr@s:~$ echo 'Ahoj, $MENO'
Ahoj, $MENO

Uloženie výsledku behu programu do premennej

Neraz sa stáva, že potrebujete spustiť program, ale nechcete výsledok zapísaný do štandardného výstupu len zobraziť, ale ho uložiť do premennej pre ďalšie použitie.

Chcete si poznačiť aktuálny dátum v slovenskom formáte do premennej?

DATUM=$(date +"%d. %m. %Y")

Všetko, čo je vo vnútri $(...) sa spustí v rámci podshellu (subshell) a výsledok, ktorý by sa za obvyklých okolností vypísal na konzolu, sa priradí do premennej DATUM.

Iný príklad: vypíšte aktuálne koordináty používateľa v tvare používateľ@názov_servera:

USERNAME=$(id -un)@$(hostname)

A následne:

echo $USERNAME

Výsledok bude:

novotnyr@s.ics.upjs.sk

Staršia syntax

Historicky funguje aj syntax cez spätné apostrofy (backticks), ktorá robí presne to isté, ibaže je neprehľadnejšia (backticky sa ťažko hľadajú na klávesnici ;-) a hlavne má problém s prípadným vnáraním subshellov.

DATUM=`date +"%d. %m. %Y"`

Ďalšie špeciálne premenné

Parametre z príkazového riadka

Ak vyrábame viacriadkové skripty, nezriedka ich chceme volať z príkazového riadka s parametrami. Urobme si napr. skript, ktorý zistí cestu k domovskému priečinku používateľa zo vstupu. Fiktívne volanie by mohlo vyzerať:

./homedir.sh novotnyr

A výstup:

/home/novotnyr

Založenie skriptu

Založme skript

S=homedir.sh; touch $S; chmod +x $S; nano $S

a vytvorme jeho prvú verziu

#!/bin/sh
LOGIN=novotnyr
cut -d":" -f6 < /etc/passwd | grep "$LOGIN"

Jednoduchý skript so zadrôtovaným menom používateľa bude zatiaľ stačiť. Všimnime si, ako sme uviedli premennú LOGIN do úvodzoviek, aj keď to nebolo potrebné. Efekt sa prejaví o chvíľu.

Premenná z príkazového riadka

Čo ak by sme chceli hodnotu získavať z príkazového riadka? Shell dáva k dispozícii premenné 19 s hodnotami rovnými prvému až deviatemu parametru z príkazového riadka. Upravme skript:

#!/bin/sh
LOGIN=$1
cut -d":" -f6 < /etc/passwd | grep "$LOGIN"

Do premennej LOGIN sa vloží prvý parameter z príkazového riadka.

Čo sa však stane ak zabudneme na parameter, teda vykonáme len ./homedir.sh? Skript vypíše cesty k domovským priečinkom pre každého používateľa. Ale prečo?

V prvom riadku sa $1 expanduje na prázdny reťazec a následne sa druhý riadok expanduje na

cut -d":" -f6 < /etc/passwd | grep ""

To znamená, že grep bude hľadať prázdny znak a ten sa nájde všade.

Túto chybu však napravíme nabudúce, keď si ukážeme pokročilé finty s expanziou premenných.

Pridaj komentár

Vaša e-mailová adresa nebude zverejnená. Vyžadované polia sú označené *