Ú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!
- Pri zapisovaní do premennej nepoužívame dolár!
- 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é 1
až 9
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.