Kedves Fórumozók!
A következő shell scriptet faragom éppen, de nem akar úgy működni, ahogy azt én szeretném:
OK=0
WARNING=1
CRITICAL=2
UNKNOWN=3
append() {
if [ -z "$1" ]; then
echo "$2"
else
echo "$1; $2"
fi
}
FS_USAGE=$( df -k | /usr/xpg4/bin/grep -E "swap|^/dev" | awk '{print $6,$5;}' )
FS_PAIRS=$( echo "$FS_USAGE" | tr -d % )
WARNINGS=""
CRITICALS=""
echo "$FS_PAIRS" | while read FS US; do
echo "$FS:$US"
if [ "$US" -gt 5 ]; then
echo "FIRED!"
CRITICALS=$( append "$CRITICALS" "$FS: $US%!!!" )
echo "$CRITICALS"
else
if [ "$US" -gt 85 ]; then
WARNINGS=$( append "$WARNINGS" "$FS: $US%" )
fi
fi
done
echo "/${CRITICALS}/"
echo "/${WARNINGS}/"
echo "/$CRITICALS/"
echo "/$WARNINGS/"
if [ "$CRITICALS" ]; then
echo "CRITICAL: $CRITICALS"
exit $CRITICAL
fi
if [ "$WARNINGS" ]; then
echo "WARNING: $WARNINGS"
exit $WARNING
fi
echo "OK"
exit $OK
A kimenet pedig:
/:3
/boot:10
FIRED!
/boot: 10%!!!
/var:11
FIRED!
/boot: 10%!!!; /var: 11%!!!
//
//
//
//
OK
A problémám pedig konkrétan az, hogy a CRITICALS, és WARNINGS változóba szeretném gyűjteni az adott felhasználási küszöbnél magasabb telítettségű FS-eket, és a ciklus vége után a szkript mintha "elfelejtené" a változó tartalmát. Ez valószínűleg azért lehet, mert egy azonos nevű lokális változót hoz létre a script, és a blokk vége után az értéke elveszik.
De itt nem függvényről, vagy szubrutinról van szó, és a változót direkt létrehoztam még a ciklus előtt. Amúgy is egy blokkon belül alapból nem kellene lokális változót létrehoznia, csak akkor, ha előtte a local kulcsszót használom. Nemde? Hogy van ez?
- 9081 megtekintés
Hozzászólások
röviden: a subshell-ben módosított változó érték nem kerül "fel" automatikusan a subshell-t indító környezetbe.
- A hozzászóláshoz be kell jelentkezni
De a ciklusmag a fenti esetben miért subshellben fut? Alapból nem gondolom, hogy ezt kellene tenni neki...
-------------------------------------------------------------------------------
Az életben csak egy dolog a szép, de az épp nem jut eszembe.
Slackware Linux 13.37 | 2.6.39.4-janos
- A hozzászóláshoz be kell jelentkezni
hint: while subshell-t indít
- A hozzászóláshoz be kell jelentkezni
Hint: nem, viszont mivel pipe mögött van, ezért alshell.
- A hozzászóláshoz be kell jelentkezni
Igen, sorry, pipe-ot gondoltam, és while-t írtam le, nem tudom miért :)
- A hozzászóláshoz be kell jelentkezni
Mert a whale skót akcentussal közel ugyanaz, mint a while, és a whale kilégzéskor úgy néz ki, mintha szintén pipe-ot használna. Ráadásul a whale-eket ugyanúgy save-elni kell, mint a szkripteket.
Ravasz dolog ez a pszichológia.
- A hozzászóláshoz be kell jelentkezni
- A hozzászóláshoz be kell jelentkezni
röviden: mert "process group". Kell neki egy "group leader", ami egy subshell.
- A hozzászóláshoz be kell jelentkezni
Meh, itt írnak róla
-------------------------------------------------------------------------------
Az életben csak egy dolog a szép, de az épp nem jut eszembe.
Slackware Linux 13.37 | 2.6.39.4-janos
- A hozzászóláshoz be kell jelentkezni
A pipe hasznalata miatt.
- A hozzászóláshoz be kell jelentkezni
Azaz: a pipe és a ( ) mindig subshell. Nem kell hozzá while.
- A hozzászóláshoz be kell jelentkezni
Azt hiszem, az igazi választ megadták.
Annyit tennék hozzá, hogy annak nyomós oka kell, hogy legyen, hogy az ember feldolgozásra visszatérjen a shellbe, ha már egyszer elindított egy awk processzt - itt nem látok ilyen okot.
- A hozzászóláshoz be kell jelentkezni
Az ok: nem vagyok "awk-programozó".
-------------------------------------------------------------------------------
Az életben csak egy dolog a szép, de az épp nem jut eszembe.
Slackware Linux 13.37 | 2.6.39.4-janos
- A hozzászóláshoz be kell jelentkezni
De, ha fenntartás nélkül neki mersz menni ilyen shellszkriptnek, akkor az vagy, csak nem tudsz róla, mert (valószínűleg) nem tudod, hogy az awk nyelvi lehetőségeit átfutni es awk-programot írni MESSZE kisebb erőfeszítés, mint báremlyik shellből kihozni valami hasznosat.
De most már tudod.
- A hozzászóláshoz be kell jelentkezni
Igen, nagyjából tisztában voltam vele, hogy az awk sokkal többre képes mint, amire használom. Azt is írják róla, hogy inkább egy egész programnyelv, mint egy egyszerű parancs.
Egyelőre csak egy sed tutorialt olvastam el, az awk mélyebb/alaposabb áttanulmányozására még nem tudtam sort keríteni.
-------------------------------------------------------------------------------
Az életben csak egy dolog a szép, de az épp nem jut eszembe.
Slackware Linux 13.37 | 2.6.39.4-janos
- A hozzászóláshoz be kell jelentkezni
Ha belepislantottál már a sedbe is, akkor az awk már nem nagyon mond újat, ui. awk =~ sh + grep + sed, megszelidítve.
- A hozzászóláshoz be kell jelentkezni
Valami ilyesmi..?
echo "$FS_PAIRS" | (
while read FS US; do
echo "$FS:$US"
if [ "$US" -gt 5 ]; then
echo "FIRED!"
CRITICALS=$( append "$CRITICALS" "$FS: $US%!!!" )
echo "$CRITICALS"
else
if [ "$US" -gt 85 ]; then
WARNINGS=$( append "$WARNINGS" "$FS: $US%" )
fi
fi
done
echo "/${CRITICALS}/"
echo "/${WARNINGS}/"
echo "/$CRITICALS/"
echo "/$WARNINGS/"
if [ "$CRITICALS" ]; then
echo "CRITICAL: $CRITICALS"
exit $CRITICAL
fi
if [ "$WARNINGS" ]; then
echo "WARNING: $WARNINGS"
exit $WARNING
fi
)
retval=$?
[ $retval -gt 0 ] && exit $retval
vagy
set -e
echo "$FS_PAIRS" | (
while read FS US; do
echo "$FS:$US"
if [ "$US" -gt 5 ]; then
echo "FIRED!"
CRITICALS=$( append "$CRITICALS" "$FS: $US%!!!" )
echo "$CRITICALS"
else
if [ "$US" -gt 85 ]; then
WARNINGS=$( append "$WARNINGS" "$FS: $US%" )
fi
fi
done
echo "/${CRITICALS}/"
echo "/${WARNINGS}/"
echo "/$CRITICALS/"
echo "/$WARNINGS/"
if [ "$CRITICALS" ]; then
echo "CRITICAL: $CRITICALS"
exit $CRITICAL
fi
if [ "$WARNINGS" ]; then
echo "WARNING: $WARNINGS"
exit $WARNING
fi
)
Ez utóbbi szebb, de óvatosabbnak kell lenni vele..
- A hozzászóláshoz be kell jelentkezni
Nekem ez személy szerint szimpatikusabb, de azért köszönöm a javaslatokat!
-------------------------------------------------------------------------------
Az életben csak egy dolog a szép, de az épp nem jut eszembe.
Slackware Linux 13.37 | 2.6.39.4-janos
- A hozzászóláshoz be kell jelentkezni
Nekem is, csak nem jutott eszembe. :) Viszont ha csak egyszerű echo az stdin, akkor ez még frappánsabb:
something=bar
while read line; do
foo=$line
echo foo1: $foo
done <<<$something
echo foo2: $foo
- A hozzászóláshoz be kell jelentkezni
subscribe
- A hozzászóláshoz be kell jelentkezni
sub
--
>'The time has come,' the Walrus said<
- A hozzászóláshoz be kell jelentkezni
Ha muszáj a pipe, akkor úgy hozok ki belőle változót, hogy a pipe-on belül stdout-ra küldöm a cuccot, s az egész kócerájt egy helyettesítésként változó értékadásába írom valahogy emígyen:
#!/bin/bash
eval "a=(`ls -l |\
while read; do
echo \"'$REPLY' \"
done`)"
echo "${a[5]}"
exit 0
Tudom, rondácska egy értékadás, ugyanakkor egyből egy tömböt definiál, így sok változót ki lehet így hozni. Valami ilyesmi lesz belőle:
a=('érték0' 'érték1' 'érték2' 'érték3' 'érték4' 'érték5' )
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Több kérdésem is van:
- minek az eval?
- minek a pipe jel után a backslash? (eleve idézójelben vagy, onnan meg mindenestül kiesik; de ha nem lennél idézőjelen belül, akkor se kéne)
- biztosan ls -l -et akartál írni? Mert szerintem akkor lesz benne egy halom fölösleges szemét, nem csak fájlnevek, de tény, ez akár kellhet is.) Úgyhogy koncentráljunk az első két kérdésre.
- A hozzászóláshoz be kell jelentkezni
Ha nincs eval, akkor az 'a' változó értéke egy string lesz, ami
('érték0' 'érték1' 'érték2' )
szerkezetű, de ez csak egy string. Nekünk viszont az kell, hogy a shell kifejtse ezt, mintha leírtuk volna így, mint tömb definíciót:
a=('érték0' 'érték1' 'érték2' )
Igazad van abban, hogy a pipe után nem kell a backslash. Azért írtam, hogy egy sorban legyen a while-lal, a newline karaktert ignorálja. Valahogy az volt bennem, egy sorba írnám, de az átláthatóság miatt mégis több sorban írom.
Általában akartam megmutatni, hogy pipe-ból, azaz másik process-ből, elszeparált memóriaterületről bash-ben hogyan lehet több változót kihozni a szülő shellhez. Azért választottam az ls -l
parancsot, hogy az eredményben legyenek szóközök is, s látszodjék, a szóközöket tartalmazó értékek átadása is sikeres. Persze, hogy az, hiszen a $REPLY
köré tettem literális aposztrofokat, amelyek az eval kapcsán a shelltől teljes elzárásként fog értelmeződni, persze mindez már a $REPLY
helyettesítésének megtörténte után.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Még mindig nem értek egyet.
a=(`ls -l`)
eval a=(`ls -l`)
nekem tök ugyanazt az eredményt adja(*), megfejelve azzal, hogy ha az ls kimenetében lennének spéci karakterek, akkor az általam javasolt evaltalan formában megmaradnának a tömbváltozó valamely elemének értékeként, míg eval esetén számíthatsz egy további feldolgozásra. (Azt meg nem látom, hogy az én ls-példám, és a te sokkal bonyolultabb parancsod között az adott esetben mitől lenne különbség.) Elvben meggyőzhető vagyok, de a fenti okfejtésed nekem nem OK.
(*) azaz a és aa nem egy string értékű változó, hanem egy tömbváltozó egymillió elemmel.
Szerk: szerintem akkor lenne igazad a sztringben, ha a zárójelek el lennének takarva.
- A hozzászóláshoz be kell jelentkezni
Nekem eval nélkül szétesett a szóközök mentén. Tudom, ez nem igazán érv, gondolkodom majd még rajta, ugyanakkor eval nélkül nem sikerült egyelőre az eredeti és egyben kívánt eredményt előállítani.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
> Nekem eval nélkül szétesett a szóközök mentén.
Nekem meg nem esett szét:
l=("`ls -al`" "`ls -al /`") ; for x in "${l[@]}" ; do echo $x ; echo '<<<>>>'; done
- A hozzászóláshoz be kell jelentkezni
Igyekeztem minimalista szinten változtatni a te kódodon, de nekem látszólag megy, azaz a teljes ls kimenet minden szava egy tömbelem.
#!/bin/bash
a=(`ls -l |
while read; do
echo "$REPLY"
done`)
printf '!!! %s !!!\n' "${a[@]}"
exit 0
Ha neked az kell, hogy a szóközöknél ne essen szét, hanem csak a soremeléseknél, akkor sokkal egyszerűbb megoldás az értékadás előtt átállítani az IFS változót:
#!/bin/bash
IFS='
'
a=( ...
mondjuk így.
- A hozzászóláshoz be kell jelentkezni
Persze, hogy nem minden szónak kell egy tömbelemnek lennie. A példában gondolom látszott, hogy egy-egy változót egy-egy echo paranccsal írtam a tömbbe, ahol a változó értéke tartalmazhat szóközt is. Éppen ezért írtam a $REPLY
köré aposztrofokat. Az eval-t pedig éppen arra használtam, hogy az első alkalommal generálom a stringet:
a=('value 0'
'value 1'
'value 2'
)
A value n értékek jelen példámban a $REPLY változóból kerülnek a stringbe. Erre ráengedve az eval-t, a shell ezt megkapja értelmezésre, s így definiálásra kerül egy tömb, melynek elemei aposztrofok által határolt értékek, s éppen ezért szóköz, tabulátor, newline mentén nem esik szét az érték.
Nem tudom, az IFS átdefiniálása mennyivel egyszerűbb, nekem nem tűnik annak az eval-lal szemben. Ugyanakkor töredelmesen bevallom, hogy azért sem szoktam hozzányúlni, mert nem tudom, pontosan mire is vonatkozik az IFS. Kipróbáltam, magára a scriptre, tehát arra, hogy az interpreter felolvassa a scriptet, nincs hatással. Ezek szerint a read-re vonatkozik.
Ugyanakkor én pipe-ból való változó kihozására írtam általános példát, arról szó sincs, hogy szóközt tartalmazó változó read által keletkezik, lehet az úgy is, hogy pipe-ból read-del olvassuk a sort, majd valamilyen algoritmussal előállítunk egy változót, amelyik szóközt tartalmaz.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Elnézést, hogy ide ugatok, de...
Az IFS, teljes nevén Internal Field Separator, puritán fordítva "belső mező elválasztó". Amikor átírod, az utána jövő sorokban ez fog mező elválasztóként funkcionálni tömböknél, amikor a bash használja az adott változót (tévedés joga fenntartva, én így értelmeztem, eddig :) Ha tévedek, javítsatok :) )
Lényegében a bash lelki világán nem változtat, a felhasználáson kényelmesít, a "beolvasott értékekre" van kihatással, pontosabban definiálod, hogy mi legyen utána a tömbhatárolónak vett karakter :)
(Alapértelmezetten az IFS értéke tetszőleges whitespace, amibe beleesik a new-line is... Ezért nem is szükséges a visszatérési értéknél átállítani, ha sorokba rendezve adod át az outputodat :) )
Egy egész kellemes kis leírás található róla itt : http://tldp.org/LDP/abs/html/internalvariables.html
Probléma mentes használásához célszerű először lementeni egy másik változóba az aktuális értéket, majd utólag visszaállítani, pl.:
OLD_IFS=IFS
IFS="
"
#A részlet, ahol kell nekünk az átértelmezés
IFS=OLD_IFS
Leginkább az IFS használata, számomra, olyankor kellemes, amikor "egy változóba" akarok több paramétert zsúfolni, és utána kényelmesen értelmezni ezt.
Inkább összetett script kötegeknél van értelme - én például egy plugin szerűség implementálásánál használtam fel -, vagy, amikor pl. egy csv jellegű filet akarsz kényelmesen kezelni :)
Üdv,
LuiseX
- A hozzászóláshoz be kell jelentkezni
Egyébként most látom, hogy akkor csinálná pontosan azt, amit írtam, ha
echo -n \"'$REPLY' \"
Lenne az a bizonyos sor. Másik lehetőségként, ha a tömb elemei között newline karakterrel szeparálok, felesleges a szóköz, tehát jó az
echo \"'$REPLY'\"
is. Persze, ahogy eredetileg írtam, úgy is jó.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
- A hozzászóláshoz be kell jelentkezni
Van egy trükk while-nél... így próbáld meg:
while read FS US; do
echo "$FS:$US"
if [ "$US" -gt 5 ]; then
echo "FIRED!"
CRITICALS=$( append "$CRITICALS" "$FS: $US%!!!" )
echo "$CRITICALS"
else
if [ "$US" -gt 85 ]; then
WARNINGS=$( append "$WARNINGS" "$FS: $US%" )
fi
fi
done < <(echo "$FS_PAIRS")
- A hozzászóláshoz be kell jelentkezni
közben észrevettem, hogy más is írta, de már mind1..
- A hozzászóláshoz be kell jelentkezni