Hogyan kell ezt shell script-ben... if()

Szóval hogyan kell helyes szintaktival megírni shell script-ként? Bash van. Kicsit már megkopott a tudásom és a google sem nagyon akar segíteni.

if (($foo == "string" &&  $foo != $bar) || ($foo != "string" && $foo == $bar))

A 'foo' és a 'bar' is string.

Hozzászólások

Szerkesztve: 2024. 04. 05., p – 21:27

Átíró képlet:

  • az egyenlőség vizsgálata `==' helyett `=';
  • az AND kapcsolat: -a;
  • az OR kapcsolat: -o;
  • a zárójeleket escape-eld;
  • a változókat idézőjelezd, hogy ne érjen meglepetés pl. üres string vagy szóközöket tartalmazó érték (stb.) esetén;
  • az egész kifejezés legyen szögletes zárójelben.

asszem ennyi:

if [ \( "$foo" = "string" -a "$foo" != "$bar" \) -o \( "$foo" != "string" -a "$foo" = "$bar" \) ]
then
  echo "hurra"
else
  echo "sux"
fi

(Ezt most csak ész nélkül átírtam, nem néztem, hogy logikailag jó-e...?)

Köszi. A neten hol azt írják, hogy kerüljük a '-a' és '-o' megoldást, hol azt, hogy ez az egyedüli üdvözítő. Közben kitököltem én is, meghagytam a dupla egyenlőség jelet (nekem így jobban olvasható) viszont ahhoz dupla szögletes zárójel kell. Az idézőjelre írt tanácsodat viszont megfogadtam.

„Az összeomlás elkerülhetetlen, a katasztrófa valószínű, a kihalás lehetséges.” (Jem Bendell)

Nem kell kerülni az -a és -o megoldást, de igen, sokan nem szeretik. Az a POSIX szabványos, és megy szigorúbban POSIX kompatibilis shellekben, dash, ksh, pkdsh, stb.. Az && || modern shell-szintaxis, Bash, zsh támogatja, sokaknak ez olvashatóbb, de ugyebár akkor függőség. Ha BSD-ket, Illumnos-rendszert, stb.-t is használsz, akkor fontos lehet.

Én egy ideje nem írok szándékosan Bash-specifikus scriptet, meg script elejére kifejezett #!/bin/bash vagy #!/usr/bin/env bash fejlécet, hanem szigorúan #!/bin/sh, és csak szigorúbban POSIX szabványos megoldásokat használom, előkészítve a FreeBSD-re átállást, meg hogy ne legyek Bash-hoz, Linux-specifikus dolgokhoz kötve. Persze hátránya is van, mert a Bash sok kényelmi funkciót kínál, tömbkezelés, jobb feltételvizsgálat, jobb glob-kezelés, a read-nek is nagyon hasznos a -n és -t kapcsolója (ezt sajnos elég nehézkes gányolással lehet csak POSIX-kompatibilisen megoldani), így sokszor abban könnyebb elegánsabb, olvashatóbb kódot írni.

Sokszor nem csak a script szintaxisán múlik, meg a konkrét shellen, hogy valami hordozható legyen, hanem meghívott parancsoknál is figyelni kell, mert ilyen free, lsblk, blkid, nincs pl. BSD-ken, vagy csak külön letöltött implementációk vannak rá, meg watch sincs, azt is külön gnuwatch-ként kell telepíteni, illetve problémás lehet a bsdtar, gnutar eltérései, a GNU grep, sed, stb. speciális, nem szabványos kapcsolói, eltérő initrendszerek, stb.. Van egy csomó buktatópont, ahol el lehet csúszni, és ezt nagyon elméletileg kiszűrni se lehet, csak gyakorlatban különböző rendszereken és shellekben kipróbálással.

A computer is like air conditioning – it becomes useless when you open Windows.” (Linus Torvalds)

Szerkesztve: 2024. 04. 06., szo – 10:49

Kb ennyi:

if [[ ($foo == "string" && $foo != $bar) || ($foo != "string" && $foo == $bar) ]]; then
    echo "OK."
else
    echo "NEM OK."
fi

Talisker Single Malt Scotch Whisky aged 10 years - o.k. Yamazaki is playing as well :)

igen, legtöbb esetben ${foo}  nomenklatúrát érdemes használni.
Az eredeti kifejezéshez képest nem akartam sok változtatást bevezetni.
Egyébként a [[ esetén nem szükséges a változókat "" közé rakni ha jól emlékszem (olvastam valahol, csak nem tudom hol :) )
... ezeket mindig újra le kell csekkolnom, mert pont olyan sűrűn írok komplexebb shell scripteket, hogy az ilyesmiket pont elfelejtem és fel kell frissíteni :)

A fenti megoldás egyébként rendesen megy üres string esetén is.

Mondjuk igen, a paranoiás scriptelés esetén az a biztos, ha "${foo}" stb. van használva.

Talisker Single Malt Scotch Whisky aged 10 years - o.k. Yamazaki is playing as well :)

> Egyébként a [[ esetén nem szükséges a változókat "" közé rakni ha jól emlékszem (olvastam valahol, csak nem tudom hol :) )

Annyira, hogy - ahogy már itt is elhangzott - egyenest ellenjavalt, mert mást fog eredményezni, mint amire számít az ember.

A shellben adott helyre azért javasolt idézőjelet rakni, hogy a különféle helyettesítések (úgy is mint: változó helyettesítés: $x ; parancs helyettesítés: $( p ) ; aritmetikai-helyettesítés: $(( k )) vagy $[ k ] ; vagy a hullámvonal (tilde) helyettesítés: ~u ) eredményéből adódó szóközök, tabulátorok és soremelések kapcsán előforduló szavakra tördelést vagy éppen az üres sztringre helyettesítésből adódó paraméterelvesztést elkerüljük. Illetve még egy gyakori szükséglet, amikor a fájlnév-globbing (joker-karakterek, *?) helyettesítést szeretnénk kikapcsolni az adott helyen ( echo L* vs echo "L*" ).

Nem tudom mennyire szoktál ilyesmit olvasni, de pl. a manualban benne van.

man yash ezt írja a [[-ről szóló részben:

"When the command is executed, operand words are subjected to the four expansions, but not brace expansion, field splitting, or  pathname expansion."

Tehát a szavakra tördeléstől és a joker-karakterek helyettesítésétől nem kell félni.

man ksh93

"A  conditional  expression is used with the [[ compound command to test attributes of files and to compare strings.  Field splitting and  pathname  expansion are not performed on the words between [[ and ]]."

Ez ugyanaz, mint a yash-nál, csak más szavakkal.

man bash:

"If the pattern is stored in a shell variable, quoting the variable  expansion forces the entire pattern to be matched as a string." (és nem mint pattern / regexp)

Legtöbb modern shellben van "test kifejezés" parancs (és a vele ekvivalens [ kifejezés ] forma is használható), valamint a [[ kifejezés ]] illetve a (( kifejezés )) konstrukciók is használhatók, de vannak köztük nem annyira vicces különbségek. Az emberek jelentős többsége nem érti, hogy a fájlnév globbing ( ?*[] ) és az ezeket használó pattern-ek (pl. a "case" parancsban) rohadtul nem egyeznek meg a regexpekkel, és kevesen tanulják meg, hogy az előbbi van a shellben, az utóbbi pedig nincs. És aztán jön a shell-ben a [[ parancs és annak = (vagy ==) operátora, amelynek jobb oldalán pattern szerepel; majd utána durva arculcsapásként a  =~ operátor, aminél már nem pattern, hanem extended regexp a jobb oldal, és kiderül, mégis van regexp (is) a shellben is. Nem bonyolultabb, mint más programozási nyelvek: pl. =, ==, ===, csak kevesebben veszik a fáradságot odafigyelni / megtanulni a különbségeket.

Lol. Ez ember mindig tanul valamit. :-)

Én a [[ formát csak és kizárólag a string alapú feltételeknél használom, és szigorúan macskakörmök közé teszem a string változókat. Kivétel ha regexp. :-)

Szerintem bash -ban sokan megszokás szerint programozunk, ezért tűnik más kódja gyakran hebehurgyának.

Én például megszoktam, hogy if [[ "$valami" != "" ]]; then ... amit tudom, hogy a -z és a -n kapcsolókkal rövidebben is ki lehetne fejezni, de nekem így valahogy olvashatóbb. Másnak meg nem. Asszem pont a xz -es történet kódjában láttam, hogy ott az "x$var" == "x" formát használta, ami megintcsak működik, de mégsem elegáns, hogy csak azért teszek bele workaroundként egy kamu karaktert, mert elbotlik a kifejezés, ha üres stringet kap ott a feldolgozó.

Mivel az ember ritkán ír performancia-kritikus kódot bash -ban, így a különféle formák között nincs jelentős különbség a végrehajtás terén, viszont az olvashatóság, illetve a stabilitás felől nézve igenis vannak jobb és rosszabb megoldások. Az ember a saját, megszokott kifejezéseit megtanulja körbebástyázni, tudja, hogy mire kell figyelni, de amikor más kódjába kell nyúlni, és az úriember ilyenkor ugye hasonul a környezethez, akkor probléma lehet, ha a mások által előszeretettel használt szintaktikát nem ismeri teljeskörűen. Ilyenkor jön ki, hogy micsoda taposóaknákkal van tele a shell programozás. :-)

Persze sok fura forma nem öncélú, hanem teszem azt a POSIX miatt írta úgy a költő, hiszen nagyon nem mindegy, hogy valaki egy céges kódot ír egy adott gép(család)ra, ahol fix a distro és még az OS és a bash verzió is, vagy arra számít, hogy az adott kódot boldog-boldogtalan mindenféle oprendszeren és parancsértelmezőn fogja futtatni, ergo nem lehet benne bashizmus.  

Írtad:

A 'foo' és a 'bar' is string.

Itt arról van szó, hogy a foo és bar változók tartalmazzák annak a változónak a nevét - tehát van benne még egy indirekció -, amelyre kíváncsi vagy?

a=5; b=a; echo "$a", "$b", "${!b}"
5, a, 5

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Nem. A $foo egy string tömb elemei, a $bar a program egy paramétere, a $string pedig valójában a $foo tömbjének az utolsó eleme csak azért írtam fixen mert fogalmam sincs, hogyan kell egy tömb utolsó elemére hivatkozni. Mivel a tömb nem változik, így is jó. Az egész egy for cikluson belül van, ezáltal változik a $foo.

Ha a paraméter($bar) egyezik a $string-gel($foo[latest]), akkor az összes többi elemre matcholni kell, ha nem egyezik, akkor csak arra amivel egyezik.

„Az összeomlás elkerülhetetlen, a katasztrófa valószínű, a kihalás lehetséges.” (Jem Bendell)

Just for fun

 

Kérdés:

Szóval hogyan kell helyes szintaktival megírni shell script-ként? Bash van. Kicsit már megkopott a tudásom és a google sem nagyon akar segíteni.

if (($foo == "string" &&  $foo != $bar) || ($foo != "string" && $foo == $bar))

A 'foo' és a 'bar' is string.

 

ChatGPT válasz:

A bash shell scriptben az if feltételek kiértékeléséhez a [[ ... ]] szerkezetet használhatod, ami kibővített szintaxisú, és több lehetőséget kínál a feltételek megadására. Az && és || operátorok is működnek, de fontos, hogy a string összehasonlításokhoz a = vagy == operátort használd. Itt van a helyes szintaktika a megadott feltételre:

 
if [[ ($foo == "string" && $foo != $bar) || ($foo != "string" && $foo == $bar) ]]; then
    echo "A feltétel igaz."
else
    echo "A feltétel hamis."
fi

Fontos megjegyezni, hogy a változók értékét a feltételekben érdemes idézőjelek között megadni, különösen akkor, ha azok stringek. A == operátorral végezhető az egyenlőségvizsgálat, és a != az egyenlőtlenségvizsgálatra szolgál.

Ezenkívül a bash scriptekben ajánlott az alábbiakat figyelembe venni:

  1. Megfelelő változó inicializálás: Győződj meg róla, hogy mindkét változónak van értéke, mielőtt az if feltételben használnád őket.

  2. Helyes operátorok használata: Ellenőrizd, hogy a változókat az értékük alapján megfelelő operátorokkal hasonlítod össze.

  3. Megjegyzések hozzáadása: Ha lehetséges, adj megjegyzéseket a kódhoz, hogy később könnyebb legyen megérteni és karbantartani.

  4. Hibakezelés: Ellenőrizd a váratlan hibákra való felkészülést, például a változók értékeinek meglétét.

Ez a shell script megfelel a megadott feltételnek, és az if ág a feltétel igazságára vonatkozó kódot hajtja végre, míg a else ág a feltétel hamissága esetén hajtódik végre.

Király vagy! Eszembe sem jutott ChatGPT-t megkérdezni. Közben már én is rájöttem a szintaktika logikájára. Viszont óvatosan azzal az idézőjelekkel. Belefutottam egy ilyenbe, mivel fentebb is javasolták az idézőjeleket, ezért használtam:

if [[ "$string"  =~ "$REGEX" ]]

Na ez így nem működik, ilyen esetben tilos idézőjelbe rakni a változókat.

„Az összeomlás elkerülhetetlen, a katasztrófa valószínű, a kihalás lehetséges.” (Jem Bendell)

Nos, a bash nem egy bolondbiztos programnyelv, hanem egy rakas binaris meg builtin katyvasza. Viszont kis dolgokra hihetetlenul kezreallo dolog, mondhatnam, hogy annyire programnyelv, amennyire az Excel adatbaziskezelo. Nem az, de a fel vilag arra hasznalja. :-)

Az idezojel egy vedelmi mechanizmus az ures vagy whitespace tartalmu valtozok esetere. A kapcsoszarojelek hasonloan defenziv okokbol hasznosak, de string osszefuzesnel gyakran muszaj is.

A nyelvben egy csomo dolog ugy van belehakolva, hogy ihaj, ilyen a fenti peldad, a regex kezeles szamit a globbingra. Hasonlo, mint amikor substring egyezest keresel, vagyis if [[ "ez valami szoveg" == *"valami"* ]]; then ... akkor a csillag muszaj, hogy az idezojelen kivulre keruljon, persze belul tobbnyire valtozo van, ez most csak a pelda miatt fix szoveg.

En egy csomoszor mikozben kodolok  megkerdezem a chatGPT -t arrol amit eppen csinalok. Van amikor baromsaggal jon, ilyenkor szinte mindig a koncepciot erti felre, hogy mit akarok valojaban. De az esetek nagy reszeben azt valaszolja, amit en is gondolok az adott dologrol. Amit igazan szeretek benne, az viszont az, amikor egy uj, szamomra nem ismert modszerrel oldja meg az adott problemat.

Ha az o megoldasa elegansabb, akkor siman valtok, mert azert valjuk be, a bash rengeteg modot ad egy adott cel eleresere, es szerintem nincs olyan ember, aki minden tipusfeladatra a legoptimalisabb modot nyomja rutinbol. De mindig vegig kell gondolni amit ir, ebbol a szempontbol tok azonos a felhasznalasi modja, mint a StackOverflow -nak. A ctrl-v ctrl-c huszarok itt is pont ugyanugy megszivjak, ha nem ertik meg, hogy az ajanlott kod miert pont ugy van, ahogy. De teny, hogy alap dolgokban tenyleg ritkan teved.