Bash script tanulás

Mérnökinfós nagyfiammal próbálom megszerettetni a shell script programozást. 

Én magam sem vagyok nagymestere a műfajnak, de néhány érdekességet összeszedtünk ebben a scriptben, ami egy újoncnak még izgalmas lehet.

#!/bin/bash

LOGFILE=/mnt/VPSmgt/var/log/drbackup.log

BACKUPTAB=/mnt/VPSmgt/etc/drbackup/backuptab
# A backuptab file formátuma:
# <host>	  <backuprepo>	  <dir>					<extra parameters>
# lxc15           VPSroot         /etc
# lxc15           VPSroot         /tplxc15lxd/containers/ctdev02
# lxc15           VPSmounts       /mnt/ctdev02/usrsap
# lxc16           VPSmounts       /mnt/ctdvl01/sapdb			--one-file-system

FAILED=0

# szín konstansok - csak interaktív terminálból indítva. Echo-nál kell majd az "-e" paraméter
[ -t 1 ] && Red='\e[01;31m'
[ -t 1 ] && Green='\e[01;32m'
[ -t 1 ] && Yellow='\e[01;33m'
[ -t 1 ] && Cyan='\e[0;36m'
[ -t 1 ] && Reset='\e[00m'

# Szöveges-színes returnkód
RCstr() {
    [ "$1" -eq 0 ] && echo -e $Green"OK"$Reset || echo -e $Red"Failed"$Reset
}

# Ha interaktív terminálról fut, stdout-ra logolunk, egyébként logfile-ba
[ -t 1 ] && of=/dev/stdout || of=$LOGFILE

# Logoljuk az indítás dátumát, a script filenevét
echo -e "$Yellow`date -R`$Reset Starting $0" >>$of

# Végigolvassa a file-t sortörésnél törve
while read s
do 
    # Szétbontja az "s" változót IFS-nél törve $1..$n változókra 
    set -- $s

    # Ha nincs legalább 3 paraméter a beolvasott sorban (beleértve az üres sort is), továbblép a következő sorra
    [ "$#" -lt 3 ] && continue

    # Logolja a szétbontott értékeket, amivel indítja az aktuális mentést. Sor végén nincs LF (-n)
    echo -ne "$Cyan`date -R`$Reset \t [$@] " >> $of

    # A "-n" SSH paraméter kell, hogy ne egye meg a többi, a while ciklusban beolvasott sort a stdin-ről 
    # A végére beküldi az extra paramétereket is (backuptab 4. oszlopától)
    # Az ssh/drbackup sikeres futáskor nem ír semmit stdout/stderr-be
    # Elkapjuk a stderr-t, és string-ként dolgozzuk fel ($RS)
    RS=$(ssh -n $1 "drbackup $2 backup $3 -q ${@:4}" 2>&1)
    RC=$?
    
    # Lezárjuk a sort egy státuszjelentéssel
    echo -e $(RCstr $RC) $RS >>$of
    
    # Ha legalább egy futtatás sikertelen, jelölje az egészet annak, ez lesz a script returncode is.
    [ "$RC" -gt 0 ] && FAILED=1
    
done < $BACKUPTAB

# Kiírja a script futásidejét a SECONDS beépített változóval
echo -e "$Cyan`date -R`$Reset Backup $(RCstr $FAILED) in $SECONDS seconds" >>$of

# Ha volt legalább 1 hiba, RC=1 hibakóddal térünk vissza, egyébként 0.
exit $FAILED

Hozzászólások

A && és || egymás utáni használatával vigyázni kell!

$ echo foo && ls /x || echo fail
foo
ls: /x: No such file or directory
fail

Tehát ha a fő parancs utáni igaz-feltételként lefutó parancs futása hibás, akkor a hiba ág szintén lefut. Nyilván echo-nál és értékadásnál talán nem lesz hiba, de ha ezt lecseréled valami bonyolultabbra, okozhat kellemetlen meglepetést.

Nekem is ez tűnt fel. El kell felejteni, hogy az egy if then else szerkezet, mert nem az. Konkrétan a || előtti utolsó összetett parancs státuszkódjától függ a futása (és nem a legelső parancs státuszától). És sajnos egy echo is adhat hibastátuszt (pl. ha logfájlba írunk és túllépnénk a kvótát, vagy az ulimitet;  jogosultsági probléma, vagy épp FS-telítettség probléma lépne fel. Stb.)

<     if [ "$1" -eq 0 ]; then
< 	echo -e $Green"OK"$Reset
<     else
< 	echo -e $Red"Failed"$Reset
<     fi
---
>     [ "$1" -eq 0 ] && echo -e $Green"OK"$Reset || echo -e $Red"Failed"$Reset
Szerkesztve: 2023. 07. 05., sze – 09:07

A file olvaso ciklusban vagy helyette a mapfile vagy readarray parancs esetleg?

-- szerk 1 --

Erdemes a manual BUILT IN COMMANDS reszet olvasgatni. Es nem csak 15 evente, ahogy en szoktam :)
 

-- szerk 2 --

A valtozoimat hasznalatkor meg szoktam vedeni: $ALMA, $ALMABOR, ${ALMA}BOR

Szerkesztve: 2023. 07. 05., sze – 10:04

És a bash után felkészül a másik shell alaptartozék, amikor már a bash kevés: awk.
Félisten a cucc. Próbaképpen az egyik reinkarnációja, a gawk nagyon jól használható bonyolult floating pointos számításokhoz is, de sok máshoz is amihez a bash kevés.

Hatalmas +1 erre, illetve nem csak akkor, ha bash-ben mar nem lehet megoldani, hanem akkor is, ha mar nem praktikus (jo, a kevesbe ez is beleertheto). Ha nem 90%-ban filemuveletek vannak, jobb egy ertelmesebb nyelv.

A strange game. The only winning move is not to play. How about a nice game of chess?

Echo-nál kell majd az "-e" paraméter

... és nem használhatod helyette a sokkal szebb printf parancsot, és el fog hasalni ha az egyéb kiírandó dolog, például ``-közti parancs kimenete véletlenül épp tartalmaz egy '\' után egy speciálisan kezelt betűt.

Ehelyett sokkal elegánsabb és robusztusabb, ha a változó értéke nem a backslash majd 'e' betűvel kezdődik, hanem magával az escape karakterrel.

A $'...' konstrukcióval az értékadáskor történik a "\e" kiértékelése, a shell nyelvének részeként, nem pedig az echo parancs teszi ezt:

Red=$'\e[31m'
echo "${Red}xyz"
printf "%s\n" "${Red}xyz"

Két további tök lényegtelen nitpick:

Sosem értettem a kétjegyű 00, 01 stb. értékek használatát. A használható számok simán 100 fölé is mennek, semmi logika nincs abban hogy 2 számjegyet írjon az ember. 0 és 1 tökéletesen megfelel. Amúgy a 0, ha semmi mást nem akarsz csinálni (mint a Reset változóban) el is hagyható.

Az 1 (avagy hát 01) egyre kevesebb terminálban jelenti a szín élénk változatát (a félkövér betű helyett vagy mellett), és egyre több terminálban jelenti a félkövér betűt a szín változatlanul hagyásával. A változónév ezt akár tükrözhetné is, vagy meggondolandó lehet egy külön Bold változó bevezetése. Az, hogy a Red vastag pirosat jelent, a Cyan pedig vékony világoskéket, ekkora picinyke szkriptben oké, nagyobb léptékű projektben követhetetlen. A név ha az értéket tükrözi, akkor tükrözze precízen. Másik lehetséges megközelítés, hogy a név a funkciót tükrözi, például ErrorColor=$'\e[1;31m' stb.

Sosem értettem a kétjegyű 00, 01 stb. értékek használatát.

Nem tudom, hogy általában érted vagy csak kifejezett szituációkban. Hétköznapi (nem programozós) esetben annyi értelme mindenféleképpen van, hogy a 09 ABC-sorrendben hamarabb van, mint a 10, míg a 9 később van, mint a 10. A fájlneveimet, amiben sorszám van (pl. évfolyam), így nevezem el, így a fájlok sorrendje listázáskor "jó".

Erre a konkrét esetre írtam, ahol a használható számok jelenleg 0-tól 107-ig tartanak, és bármikor jöhet valami újabb feature egy újabb, nagyobb számra; továbbá el nem tudom képzelni, hogy sorrendezni kéne ezeket a számokat valaha is.

Nyilván ha praktikus a sorrendezés, és tudod hogy nem (vagy extrém kicsi valószínűséggel) fog 99 fölé menni, akkor logikus a két számjegy alkalmazása. Például album trackeijél a 01-foo.mp3, sorozatoknál a S01E02 stb.

A sok ">> $of" közül könnyű véletlenül elfelejteni egyet. Ha tudod, hogy minden outputot adott helyre akarsz irányítani, érdemes az "exec > outputfile" (vagy >>) konstrukcióval egyszer átrakni és onnan kezdve meg is feledkezhetsz róla (és még csak nem is kell a /dev/stdout-ot megemlíteni amit elég csúnyának tartok):

[ ! -t 1 ] && exec >> $LOGFILE

Célszerű lenne vizsgálni, hogy létezik-e (és olvasható-e) a BACKUPTAB, ha nincs, rögtön kilépni hibával.