Tömbök kezelése és használata a Shell scriptekben (4. oldal)

botond küldte be 2019. 03. 11., h – 17:11 időpontban

A 4. oldal tartalma

 

Folytatás

Az előző oldalon megismerkedtünk az asszociatív tömbökkel, most folytatjuk néhány általánosan mindegyik tömbtípussal kivitelezhető megoldással.

 

 

Tömbök exportálása

A a Bash jelenleg nem támogatja a tömbök exportálását, ezért saját megoldásra lehet szükségünk, ha például más scriptekből is el szeretnénk tudni érni a korábban deklarált tömbjeinket.

Első körben nézzük meg, hogy mi történik, ha simán exportálunk egy tömböt és egy normál változót a parancssorból:

declare -A tomb=([elso]="első" [masodik]="második elem")
valtozo="Sima string változó"

export tomb
export valtozo

Ezután ha futtatjuk az export parancsot:

export

akkor itt bent van a string változónk és még a tömbünk is, mivel ugyanabban a névtérben vagyunk:

[...]
declare -Ax tomb=([elso]="első" [masodik]="második elem" )
declare -x valtozo="Sima string változó"

Ez eddig jó, de ha egy shell scriptben futtatjuk az export parancsot:

1
2
3
#!/bin/bash
 
export
[...]
declare -x valtozo="Sima string változó"

Akkor már eltűnik a listából a tömbünk, és csak a sima változó "élte túl" az exportot, mivel a script normál futtatásakor egy sub-shellben fut le, és annak a névterébe már nem került át a tömb exportunk csak a normál változónk.

Ha viszont nem a megszokott módon futtatjuk az iménti scriptet, hanem source-oljuk a "." használatával:

. ./scriptfile_neve
[...]
declare -Ax tomb=([elso]="első" [masodik]="második elem" )
declare -x valtozo="Sima string változó"

Akkor ismét "visszakapjuk" a tömböt is. Ez pedig azért van, mert a source-oláskor a script nem sub-shellben fut le, hanem ugyanabban a shell-ben és annak névtérben, ahonnan végrehajtották a source-t, azaz a mostani esetben a parancssoréban, ahol deklaráltuk és exportáltuk a tömböt és a sima változót. Ezért ilyenkor bent vannak a létrehozott változók az exportban. Így kerülnek betöltésre például a különböző indítófájlok, mint például a .bash_profile, .bashrc, stb is.

Tehát az eddigiek alapján elmondható, hogy a normál név -> érték alapú változók exportálhatók a globális névtérbe, amiket aztán a sub-shell-ben is el lehet érni, azonban a tömböket nem lehet exportálni, azok csak a saját névtérben maradva érhetők el.

Időnként pedig szükség lehet rá, hogy egy meglévő tömböt el tudjunk érni egy lefuttatott shell scriptben is. Például amit a .bash_profile fájlunkban hoztunk létre, vagy akár a parancssorból. Erre van pár megoldás, ezek közül az egyiket fogjuk most kipróbálni.

Készítünk két scriptet a teszt kényelmesebb végrahajtásáért. Az egyiket source-olással fogjuk indítani, tehát olyan, mintha a .bash_profile vagy .bashrc fájlunkba tettük volna a tartalmát, vagy akár begépeltük volna azt a parancssorba. A másik fájlt pedig rendesen fogjuk futtatni, mint bármilyen más shell scriptet, amik ilyenkor egy subshell-ben kerülnek lefuttatásra.

Az egyik legyen mondjuk a.sh, a másik pedig b.sh

Az elsőbe tegyünk bele két tömb deklarációt: Egy indexelt és egy asszociatív tömböt, hogy lássuk mindkettővel a működést, valamint a végén lévő két sort:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
 
# Indexelt tömb létrehozása
declare -a tomb1=(első második harmadik)
 
# Asszociatív tömb létrehozása
declare -A tomb2=(
    [elso]="Első"
    [masodik]="Második"
    [harmadik]="Harmadik"
)
 
export tomb1_serialized=$(declare -p tomb1)
export tomb2_serialized=$(declare -p tomb2)

Itt a végén lévő kettő sor fogja működtetni az egészet, ami a következőt csinálja:

A declare -p <változó neve> parancs visszaadja a megadott változó deklarációs sorát, azaz azt a parancssort, amivel létre lehet hozni pontosan ugyanazt a tartalmú változót. Tömbök eseténél az összes kulcsot és értéket beleértve. Tulajdonképpen serializáljuk a tömböt egy karakterláncba, csak itt a kapott karakterlánc kimenetben maga a létrehozó parancs is benne van, ami egyébként most még jól is fog jönni.

A korábban létrehozott tömbünkkel például ezt adja ki:

declare -Ax tomb=([elso]="első" [masodik]="második elem" )

Ez itt most azért jó, mert a tömbökön lefuttatva kapunk egy-egy sort, amit egy sima string változóban is tárolhatunk. Ezt a kimenetet pedig egy $() burkolóval kiolvassuk egy másik változóba, amit string-ként már tudunk exportálni. Mindezt a fenti elméletekre alapozva tesszük, miszerint csak a normál változókat lehet exportálni a globális névtérbe, azaz a parancssorunk névterébe, míg a tömböket nem.

Ha most a "." segítségével source-oljuk a fenti fájlunkat:

. ./a.sh

Akkor lefutnak a benne lévő deklarációk és az exportok is a futtatás helyének névterében. Tehát ha most kiadunk egy export parancsot,

export

akkor a következőt láthatjuk a lista végén:

[...]
declare -Ax tomb=([elso]="első" [masodik]="második elem" )
declare -x tomb1_serialized="declare -a tomb1=([0]=\"első\" [1]=\"második\" [2]=\"harmadik\")"
declare -x tomb2_serialized="declare -A tomb2=([elso]=\"Első\" [masodik]=\"Második\" [harmadik]=\"Harmadik\" )"
declare -x valtozo="Sima string változó"

Itt megtaláljuk a sorok között a fentebb létrehozott "tomb" és "valtozo" nevű változóinkat is, de ami a lényeg, hogy a két új serializált tömbünk is megvan, amiben benne van az összes kulcs és érték, ráadásul az idézőjelek is szépen escape-elve vannak, tehát készen állnak a string-ként történő szállításra, felhasználásra.

 

 

Most érdekességképpen ha lefuttatjuk azt a fájlunkat, amiben csak az export parancs volt, akkor a következőt fogjuk látni:

[...]
declare -x tomb1_serialized="declare -a tomb1=([0]=\"első\" [1]=\"második\" [2]=\"harmadik\")"
declare -x tomb2_serialized="declare -A tomb2=([elso]=\"Első\" [masodik]=\"Második\" [harmadik]=\"Harmadik\" )"
declare -x valtozo="Sima string változó"

Pontosan amire számítottunk: Eltűnt a tömb deklarációnk, és megmaradt a három string alapú válrozó deklarációja.

Ezek alapján tehát összeállítjuk a másik parancsfájlunkat, amit "b.sh" néven nevezünk el, és beletesszük a következőket:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash
 
eval $tomb1_serialized
eval $tomb2_serialized
 
 
# Innentől már tudjuk használni az eredeti tömb változóinkat.
 
echo "tomb1 indexelt tömb kilistázása:"
for i in "${!tomb1[@]}"; do
    echo "$i: ${tomb1[$i]}"
done
 
echo
echo "tomb2 asszociatív tömb kilistázása:"
for i in "${!tomb2[@]}"; do
    echo "$i: ${tomb2[$i]}"
done

Futtathatóvá tesszük a chmod +x paranccsal majd futtatjuk. A kimenetében pedig ezt kapjuk:

tomb1 indexelt tömb kilistázása:
0: első
1: második
2: harmadik

tomb2 asszociatív tömb kilistázása:
elso: Első
masodik: Második
harmadik: Harmadik

Ezzel a módszerrel tehát felhasználhatjuk a korábban a globális névtérben létrehozott tömb változóinkat a futtatott shell scriptjeinkben is.

Az eval parancs használata biztonsági kockázatot rejt magában, mivel kintről kapott (változói névtérből) adatokat hajt végre parancsként. Ezért csak olyan helyen használjuk, ahol tisztában vagyunk a gépen lévő körülményekkel, valamint kerüljük a root-ként történő használatát. Ha nagyobb fokú biztonságra van szükségünk, akkor inkább készítsünk saját serializáló és visszaalakító algoritmust, amivel kiválthatjuk az eval parancs használatát.

 

Konklúzió

Ebben a hosszú leírásban megismerkedtünk a shell scriptekben használatos tömbökkel és azok kezelésével. Amint láthattuk, elég sok mindenre lehet használni a Bash tömbjeit, amikkel rugalmasabb scripteket állíthatunk elő. Ezeken kívül természetesen még sok mindent lehetne írni a tömbökről, ezért igyekeztem összeszedni a legfontosabb részeket, amiket a hétköznapokban is fel tudunk használni a programozás során. Remélem sikerült használható leírást összeállítani, ami segítségére lehet azoknak, akik most tanulmányozzák ezt a témát.

 

 

 

Lapozó

Ez a leírás több oldalból áll: