bash

A Wikipédiából, a szabad enciklopédiából
Bash
Bash és Bourne-shell (sh)
Bash és Bourne-shell (sh)

FejlesztőChet Ramey
Első kiadás1989. június 7.
Legfrissebb stabil kiadás5.2.21 (stabil verzió, 2023. november 9., https://ftpmirror.gnu.org/bash/bash-5.2.21.tar.gz)[1]
Programozási nyelvC
Operációs rendszermulti-platform
PlatformGNU
KategóriaUnix shell
LicencGNU General Public License
A Bash weboldala
Képernyőkép

A bash unix rendszerhéj, amely a GNU Projekt részeként készült. A futtatható fájl neve bash egy játékos mozaikszó, mely a Bourne again illetve a born again kifejezéseket rövidíti. Ez a korai Bourne shell-re utal, melyet még 1978-ban adtak ki a Unix 7 Verzió részeként. A bash-t 9 évvel ezt követően 1987-ben készítette el Brian Fox, majd 1990-ben Chet Ramey vette át a szoftver gondozását.

A legtöbb Linux rendszeren, valamint az OS X rendszereken a bash az alapértelmezett shell. A Microsoft Windows platformra is átírták a Subsystem for UNIX-based Applications (SUA) használatával, vagy POSIX emuláció biztosításával a Cygwinnel, MSYS-szal.

A bash-t a GNU GPL licenc alatt publikálták, tehát szabad szoftver.

Inicializálás[szerkesztés]

A bash inicializálása attól függ, hogy milyen módban indítják.

Interaktív login mód[szerkesztés]

Ebben az esetben a /etc/profile script kerül először végrehajtásra, ha az létezik. Ezt követően a bash megvizsgálja a ~/.bash_profile, ~/.bash_login és a ~/.profile fájlokat ebben a sorrendben, s lefuttatja közülük az első olyat, amely létezik és olvasható.

Interaktív (nem login) mód[szerkesztés]

Ha a bash interaktív nem login shellként indul, akkor a felhasználó home könyvtárában lévő .bashrc fájlt futtatja, ha az létezik.

Nem interaktív mód[szerkesztés]

Ha a bash-t nem interaktívan futtatják, például shell script-ek végrehajtásához, akkor a BASH_ENV környezeti változóban megadott inicializáló script fog lefutni induláskor.

Restricted (szigorú) mód[szerkesztés]

Ha a bash -t az rbash binárissal, vagy a --restricted kapcsolóval indítják, ezen mód kerül érvényre. A következő funkciók letiltásra vagy korlátozott használatra állnak be:

  • Könyvtárak váltása a cd paranccsal
  • A következő környezeti változók értékeinek felülírása: SHELL, PATH, ENV, és BASH_ENV
  • Parancsok végrehajtása, melyek nevében slash "/" karakter szerepel
  • Fájlnév specifikálása amely slash karaktert tartalmaz a . beépített parancshoz
  • Fájlnév specifikálása amely slash karaktert tartalmaz a beépített hash parancs `-p' opciójához
  • Funkció definíciók importálása a shell környezetből indulás közben
  • A SHELLOPTS változó értékének értelmezése a shell környezetből indulás közben
  • Kimenetek átirányítása a `>', `>|', `<>', `>&', `&>', és a `>>' parancsokkal
  • A beépített exec parancs használata a shell cseréjére
  • Beépített parancsok hozzáadása vagy eltávolítása az `-f' és a `-d' opciókkal
  • A `-p' opció használata bármely beépített parancshoz
  • A szigorú mód kikapcsolása a `set +r' vagy `set +o restricted' parancsokkal

A szigorú módot sok felhasználót kiszolgáló, biztonságosra tervezett rendszerek esetén alkalmazzák.

Speciális karakterek[szerkesztés]

A parancssorba írt információk egy része az utasítást végrehajtó programnak szól, más részük a programot indító bash-nek. Az utóbbiakat speciális karakterekkel jelezzük.

Új sor[szerkesztés]

A legegyszerűbb speciális karakter a parancssort lezáró újsor (soremelés, LF). Hatására a bash

  1. befejezi a parancs beolvasását és elemzi a parancsot
  2. elindítja a parancsban megadott programo(ka)t
  3. megvárja, amíg az véget ér, és csak azután ad lehetőséget újabb parancs begépelésére (a prompt kiírása után).

A harmadik lépés elhagyható egy másik speciális karakterrel. A parancs után írt & arra utasítja a bash-t, hogy ne várja meg a parancs lefutását (háttérben indított program; lásd még job control). Miután az indított program örökli a szülő nyitott fájljait, és a bash is tovább fut, a két program kimenete összekeveredik (hacsak a gyerekprogram nem módosítja a kapott nyitott fájlokat).

Tipikusan egy sorban egy parancs van, de lehet folytatósort írni (egy parancs több sorban), és többféle módon lehet több parancs egy sorban.

Szóhatár[szerkesztés]

Az utasítás elemzésének egyik legutolsó lépése a szavakra bontás.[2] Az utasítás első szava a végrehajtandó parancs/program neve, a többi szót a program paraméterként kapja meg.

A szóhatárt az IFS nevű környezeti változó tartalmazza, értéke alaphelyzetben helyköz, tabulátor, új sor (LF).[3] Több egymás utáni szóhatár-karakter egy szóhatárnak számít. (Üres szót pl. "" alakban lehet írni, lásd speciális karakter elrejtése.)

Speciális karakter elrejtése[szerkesztés]

Sokszor van rá szükség, hogy a speciális karaktert a végrehajtandó parancs kapja meg, más szóval: a bash ne kezelje azt speciálisan. Ennek három módja van:

  1. a \ az őt követő egyetlen karaktert nem tekinti speciálisnak. A \-jel \\ alakban írható.
  2. ' (aposztróf): az összes speciális karaktert elrejti (kivéve a lezáró újabb '-ot)
  3. ": az összes speciális karaktert elrejti, kivéve
    1. \ (lásd feljebb)
    2. $ (hivatkozás környezeti változó vagy aritmetikai kifejezés értékére)
    3. ` (parancson belüli parancs végrehajtása; lásd backtick parancs)
    4. " (az elrejtés lezárása)

Az elrejtő karakterek felváltva is használhatók. Pl. a "' szöveg a bash számára írható '"'"'", \"\', "\"'" és számos más alakban.

Speciális eset a sor végi \újsor, mely a folytatósor jele: a bash eltávolítja a parancssorból, és a következő „fizikai” sorral folytatja a parancs belolvasását.

Wildcard[szerkesztés]

A wildcard olyan karakter, mely lehetővé teszi fájlnévminta megadását:

  • *: nulla vagy több karakter a fájlnévben
  • ?: egyetlen karakter a fájlnévben
  • [karakterek]: a szögletes zárójelben felsorolt karakterek valamelyike. A karakterek helyén intervallum is megadható. Pl. [a-z0-9-] a kisbetűket, számjegyeket és a kötőjelet jelenti.
  • [^karakterek] vagy [!karakterek]]: egyetlen, a szögletes zárójelben felsoroltaktól különböző karakter.

A bash megkeresi a wildcard-ot tartalmazó szóra illeszkedő nem rejtett fájlokat, és ezek listáját helyközzel elválasztva a parancssorba illeszti a wildcard-os szó helyére.[4] Ha nincs ilyen fájl, nem módosít a parancssoron.

A bash számára speciális (nem wildcard) karakter a szó (fájlnév) elején álló tilde (~), melyet a bash a felhasználó saját (HOME) könyvtárára helyettesít.

A bash számára a ponttal kezdődő nevű fájlok rejtettek: a wildcard kiterjesztésekor nem kerülnek a listába, kivéve, ha a wildcard-os fájlnév ponttal kezdődik. Minden könyvtárban van két rejtett fájl: a . az aktuális, a .. a felette levő könyvtár neve. Ezek szükségesek a könyvtárszerkezetbeli navigáláshoz.

A fentiekből következik, hogy a Unix-programoknak nem kell felkészülniük a wildcard kezelésére, hiszen ezeket a shell – e szócikkben a bash – elvégzi, ráadásul egységes módon.[5] Ehelyett tetszőleges számú fájlt kell tudniuk feldolgozni. A hívott program nem is tudja, hogy a fájl-paramétereit wildcard-dal vagy a fájlok felsorolásával kapta-e.

Példák wildcard-ra:

  • *: a könyvtár összes nem rejtett fájlja
  • *.*: legalább egy pontot tartalmazó fájlnév
  • *\ *: legalább egy helyközt tartalmazó fájlnév (a \ jelentését lásd feljebb)
  • [A-Z]*: nagybetűvel kezdődő fájlnév
  • *.c: .c kiterjesztésű fájlnév[6]

Környezeti változók[szerkesztés]

A kernel a processz (program) adatai között nyilvántart egy memóriaterületet, melyben név=érték típusú adatok vannak. Amikor egy program elindít egy másik programot,[7] az indított (gyerek)program örökli a szülő éppen aktuális környezeti változóit. A másik lehetőség, hogy a szülő állítja elő a gyerekprogram környezeti változóit, és egy memóriaterületen átadja a gyerekprocesszt indító rendszerhívásban.

A bash mindig az utóbbi módon indít programot. Ez lehetővé teszi, hogy egyes környezeti változói helyiek legyenek, mások az indított program környezetébe is bekerüljenek.

A bash indulásakor már vannak környezeti változói az őt indító programtól (pl. a bejelentkezési eljárásból), és a bash többféle indító scriptje is létrehoz változókat. A bash lehetővé teszi a változók lekérdezését, módosítását, törlését, újak létrehozását.

Fontos tudni, hogy Unixban minden program – a bash is – csak a saját környezeti változóit tudja megváltoztatni, a szülőjéét nem, ui. annak csak a másolatát kapja meg.

A bash környezeti változói az env utasítással, az indított programoknak továbbadottak a set utasítással listázhatók ki. A két listában a nevek és értékek is szerepelnek.

A környezeti változó értékét a $név vagy ${név} kifejezés adja meg. A két alak egyenrangú; a másodikat akkor használjuk, ha a változót el akarjuk választani az őt követő betűtől vagy számtól (ami az első alakban összeolvadna névvel). Az értékre hivatkozást általában idézőjelbe teszünk, bár ez nem mindig szükséges. Az értéket a echo "$név" utasítás írja a képernyőre. Nem hiba értéket nem kapott változót használni; ennek értéke üres string.

Új helyi változót létrehozni, vagy a meglevőt módosítani a név=érték utasítással lehet. Az egyenlőségjel előtt és után nem lehet helyköz. Az értékben szerepelhet a változó régi értéke.

Ha azt szeretnénk, hogy a változót az indított programok is lássák, az export név utasítást használjuk. A név után az érték is megadható.

Változó az unset név utasítással törölhető.

A legfontosabb környezeti változók[szerkesztés]

  • HOME: a bejelentkezett felhasználó saját könyvtára.
  • IFS: szóhatár.
  • PATH: könyvtárak kettősponttal elválasztott listája. Itt keresi a kernel az indítandó programot, ha annak útvonala nincs megadva. Az aktuális könyvtár a Dos/Windows rendszerektől eltérően alaphelyzetben nincs a PATH-ban, és biztonsági okból nem is tanácsos betenni, különösen a mindenható root felhasználó esetén.
  • PS1, PS2, PS3, PS4: a prompt szövegét megadó változók (normál prompt, folytatósor prompt, a select utasítás promtja, debug prompt).
  • SHELL: a shell neve; e szócikkben /bin/bash.[8]

Példák[szerkesztés]

A PATH kibővítése a felhasználó könyvtárának bin alkönyvtárával:

export PATH=$PATH:$HOME/bin

Ugyanez leírható

export PATH+=:$HOME/bin

alakban is.

Átirányítás[szerkesztés]

Amikor Unixban egy program elindít egy másikat, a gyerekprogram – a környezeten kívül – a nyitott fájlokat is örökli. A felhasználó bejelentkezési procedúrája során több program is lefut, és ennek során létrejön három nyitott fájl azon az eszközön (terminálon, soros vonalon, stb.), ahonnan a felhasználó bejelentkezett, így a felhasználó login shellje ezeket már megnyitva kapja. A fájlok a megnyitás sorrendjében kapnak számot a kerneltől:

  • 0: standard input
  • 1: standard output
  • 2: standard hiba.

Az átirányítás célja a, hogy az indított program ne a bash-től örökölje a fenti fájlokat, hanem a parancssorban megadott fájlokat kapja meg. A három fájl egymástól függetlenül irányítható át.

A standard input átirányítása[szerkesztés]

Az egyik mód az indított program standard inputjának megadása:

program <fájl

A másik mód shell scriptben használatos: az indított program az indító scriptből vegye a standard inputot:

program <<szó
program stdin-jének első sora
második sor
...
szó

A „fájlt” lezáró szó a sor elején kell legyen. Ha a hívott program végigolvassa az standard inputját, a záró szó helyett fájl végét kap. Ha nem olvassa végig, a következő utasítás előtt a bash átugorja a be nem olvasott részt.

A kimenetek átirányítása[szerkesztés]

A standard kimenet átirányítása:

program  >fájl
program >>fájl

Mindkét alak létrehozza fájl-t, ha az nem létezett a parancs kiadásakor. Az első a létező fájl tartalmát törli, de a jogait nem változtatja. A második a fájl vége után írja program kimenetét.

Fontos tudni, hogy az átirányítás a program indítása előtt történik (lásd alább). Az első alakban fájl tartalma akkor is megsemmisül, ha a hívott program nem is létezik.

A standard hiba átirányítása teljesen hasonló:

program  2>fájl
program 2>>fájl

A bash standard kimenetére &1, a hibakimenetre &2 alakban hivatkozhatunk.[9]

Egy program mindkét kimenete ugyanabba fájlba irányítható:

program >fájl 2>&1

Egy program indítása két lépésben történik. Első lépés a fork, melynek során az indító bash teljes memóriája és processztáblája megduplázódik, azaz a bash két külön környezetben (szülő és gyerek) fut tovább. A második lépés a program kódjának betöltése a gyerek-bash helyére, és az újonnan betöltött kód elindítása.

Az átirányítás a fork fázisban történik, hogy a bash eredeti környezete ne változzék.[10] A fenti példában a >fájl fájl újranyitás az 1-es számú fájlleírón, vagyis egy rendszerhívás, mely bezárja az eredeti fájlt, és a helyére megnyit egy másikat. A 2>&1 az 1-es fájlleíró duplikálása a 2-es leíróba. Ez azt is jelenti, hogy a fenti sorrend nem felcserélhető. A 2>&1 >fájl az eredeti fájlleírót duplikálja, így az újranyitás már nem hat a hibakimenetre.

A fenti kettős átirányítás egy utasítással is végrehajtható:

program &>fájl

Gyakran használatos a kimenet átirányítására a /dev/null speciális eszközfájl, mely „elnyeli” – eldobja – az oda küldött adatokat.

Parancsok kombinálása[szerkesztés]

Pipe[szerkesztés]

prog1 | prog2

hatására prog1 és prog2 között névtelen pipe jön létre.[11] A két program külön-külön processzként indul el úgy, hogy prog1 standard kimenete átirányítódik prog2 standard bemenetére. A bash megvárja mindkét program lefutását, és prog2 visszatérési értékét teszi a $? változóba. Kettőnél több program is összeköthető pipe-pal.

Backtick[szerkesztés]

Alakja:

parancs1 ... `parancs2...` ...
parancs1 ... $(parancs2...) ...

Először parancs2 hajtódik végre, és a standard kimenete behelyettesítődik parancs1 parancssorába (a paraméterek közé). Az újsor[12] karakterek helyközre cserélődnek.

A fenti két alak annyiban különbözik egymástól, hogy a backtick-ek nem skatulyázhatók egymásba, a $(...)-k igen.

Példa:

file `which ls`

A which ls parancs végigkeresi a PATH környezeti változót, és visszaadja az ls parancs fájljának útvonalát. A file parancs kiírja a parancs típusát:

$ file `which ls`
/bin/ls: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), ...

;[szerkesztés]

A pontosvesszővel elválasztott parancsok egymás után futnak le. Az utolsó parancs visszatérési értéke kerül a $? változóba.

Példa: fájlt másolunk egy másik ablakban, és közben figyeljük a diszk telítettségét:

while true ; do df -hT ; sleep 30 ; done

&&[szerkesztés]

A && bal oldalán levő parancs lefutása után akkor hajtódik végre a jobb oldali, ha a bal sikeres volt (a visszatérési értéke 0).

Példa: a make paranccsal lefordítjuk a prog programot, és ha a fordítás sikerült, végrehajtjuk:

make && ./prog

||[szerkesztés]

A jobb oldali parancs akkor hajtódik végre, ha a bal oldali sikertelen volt (nem 0 a visszatérési értéke):

make || exit 2

Kerek zárójel[szerkesztés]

A kerek zárójelbe tett parancsok egy shellben futnak le. Ez pl. akkor lehet hasznos, ha egy fájlba akarjuk irányítani a kimenetüket, vagy egy processzben akarjuk őket futtatni háttérben. A zárójelet helyközzel kell elválasztani a parancs többi részétől.[13] A parancsok több sorba is írhatók.

Shell script[szerkesztés]

A script (egyre gyakrabban írják magyarosan szkriptnek) Unixban olyan fájl, mely egy interpretált programozási nyelv utasításait tartalmazza. Interpreterek a Unix shellek – köztük a bash – is.

Script hívása[szerkesztés]

A bash a shell scriptet ugyanúgy indítja el, mint a bináris programokat. Ez azt jelenti, hogy a script másik shellben fog futni.[14] Ez általában kényelmes, viszont a hívott shell nem tudja megváltoztatni a hívó környezetét (így a környezeti változókat sem). Ennek megoldására szolgál a pont parancs:

. shell-script
source shell-script

A két alak egyenértékű, és a script neve után paraméterek is megadhatók. A parancs hatására a bash nem indít külön programot, hanem ő maga hajtja végre a megadott shell scriptet.

A hívott script (vagy más program) visszatérési értéke a $? shell-változóba kerül. A hívott script az exit utasítás paraméterében állíthatja be a visszaadandó értéket.

Az első sor[szerkesztés]

A program indításakor a kernel[7] „belenéz” az indítandó fájl elejébe, és ebből állapítja meg az indítás módját.[15] Script esetén a fájl a #!interpreter sorral kezdődik.[16] Az interpreter nevét az új sor (LF) karakter zárja le. (Ügyeljünk rá, hogy ne legyen előtte CR, mert az is a név része lenne.) A kernel az interpretert indítja el, első paraméterként átadja a scriptfájlt. Az interpreter a többi paraméterét a hívósorból kapja, hogy átadhassa a scriptnek.

A bash-script első sora Linuxban:[8]

#!/bin/bash

A scripteket a /bin/sh sorral szokás kezdeni, ami a Bourne shellt hívja.[17] A bash a Bourne shell továbbfejlesztett változata; ha szükség van a többlet-utasításokra (pl. tömbökre), akkor az előbbi formát kell használni.

Paraméterek[szerkesztés]

A script a saját fájlnevét (útvonallal együtt) a $0 változóban kapja, a többit a $1...$9 változóban. Tíznél több paraméter a beépített shift utasítással vehető át. A kapott paraméterek számát a $# változó tartalmazza.

Scriptnyelv[szerkesztés]

Bár a shell elsősorban külső programok hívására szolgál, saját beépített utasításai is vannak, melyek önálló programnyelvet alkotnak. A harmadik generációs programnyelvek[18] szokásos utasításaira példák:

Bash függvények[szerkesztés]

Bash-függvény definiálása:

fuggveny()
{
...
}

Hívás: fuggveny par1 par2.... Függvényen belül a $1...$9 változó nem a shell script, hanem a függvény paraméterét jelenti. A függvény a return kód utasítással adhat vissza számértéket.

Aritmetikai kifejezések[szerkesztés]

Egész aritmetikai kifejezés $(( ... )) alakban írható. A műveletek azonosak a C nyelvbeliekkel. Az operandusok környezeti változók is lehetnek.

Példa:

HAROM=$((1+2))

Háttérben indított programok[szerkesztés]

Program háttérben indításakor kiíródik a processz száma, melyet a script változóba tud tenni. A script a wait procszám utasítással várhatja meg a program lefutását. A paraméter nélküli wait az összes háttérbeli programot megvárja.

Job control[szerkesztés]

A jobs utasítás kilistázza a shellből indított, még le nem futott programokat, akár eleve háttérben indítottuk őket, akár utólag, kézzel állítottuk meg és/vagy tettük háttérbe (lásd alább). A program akkor minősül „lefutott”-nak, ha befejeződött a végrehajtása, és ezután lekérdezték a státusát.[19] A lekérdezést elvégzi a jobs, de maga a bash is, mielőtt kiírja a következő utasítás promptját.

Mialatt a bash az indított program lefutására vár, a Ctrl/Z megszakítja a várakozást (SIGTSTP szignál), megállítja az indított programot, és visszaadja a promptot. A bg szám háttérben, a fg szám előtérben indítja tovább a programot, ahol szám a jobs által kiírt job sorszám.

A Ctrl/C (SIGINT szignál) befejezi az előtérben futó programot. Ha a bash fut, Ctrl/C-re eldobja az addig beolvasott sort, és új promptot ír ki.

Szignálok[szerkesztés]

A szignál segítségével az egyik Unix processz értesítheti a másikat egy esemény bekövetkeztéről. 64-féle szignál van, melyek különböző eseményeket jelezhetnek. Ha a programot nem úgy írták meg, hogy képes legyen szignált fogadni, a szignáltól függő default akció történik a szignál címzettjével:

  • befejeződik a futása
  • figyelmen kívül marad a szignál
  • a processzről core dump készül
  • a processz futása megáll
  • a processz futása újraindul.

A SIGKILL (9-es) szignál nem jut el a címzetthez: a kernel kilövi a címzett processzt (feltéve, hogy a küldőnek erre volt joga). Ha egy program elszabadul, és már szignálokra sem reagál, ez az egyetlen módja a program leállításának. Hátránya, hogy a program nem tud rendet tenni a befejeződése előtt (pl. a puffereit kiüríteni).

bash-ban a kill -l beépített parancs kilistázza a szignálok nevét és kódját.

Szignál küldése bash-ből[szerkesztés]

Szignál küldése:

kill -szám procid...

ahol szám a szignál száma vagy neve (ha elmarad, 2 = SIGTERM), melyet processzazonosítók helyközzel elválasztott listája követ. A név arra utal, hogy legtöbb esetben programok kilövésére használjuk a parancsot. A létező processzazonosítókat pl. a ps és pgrep parancs írja ki.

A killall és pkill parancs lehetővé teszi szignál küldését adott nevű és/vagy adott felhasználóhoz tartozó processzeknek.

Shell script a trap utasítással fogadhat szignálokat.

Jegyzetek[szerkesztés]

  1. bash-5.2.21.tar.gz, 2023. november 9. (Hozzáférés: 2023. november 9.)
  2. A parancssor utasításokra bontása korábbi lépésekben történik.
  3. Így lehet kiíratni: echo -n "$IFS" | hexdump -C
  4. A bash parancssorának hossza a kerneltől függ, alaphelyzetben 128 kByte.
  5. Néhány speciális program, pl. az ls és a find saját fájlkeresést használ.
  6. Unixban nincs fájl kiterjesztés: e szó a DOS-os terminológiából származik. A pont nem különleges karakter a fájlnévben.
  7. a b Lásd exec rendszerhívás-családot.
  8. a b Egyes Unixokban a bash a /usr/bin könyvtárban van.
  9. Ha a bash-ben további fájlokat nyitottunk meg, azok fájlszáma is írható ilyen alakban.
  10. A fork fázis célja éppen az indítandó program környezetének beállítása a szülő memóriaterületén levő adatokból.
  11. Névvel rendelkező pipe a mkfifo paranccsal hozható létre.
  12. Pontosabban: az IFS környezeti változó karakterei.
  13. A helyköz nélküli listát egy tömb elemeinek tekinti a bash.
  14. A Dos/windowsos rendszerekben ez éppen fordítva van. A shell a kiterjesztésből tudja, hogy shell scriptet kell hívnia, és azt ugyanazzal a shellel hajtja végre, hacsak nem használjuk a call beépített utasítást.
  15. A file paranccsal kiíratható az információ.
  16. Ez azt jelenti, hogy a script-nyelvek a #-sel kezdődő sorokat – legalábbis az első sorét – kommentnek kell tekintsék.
  17. Linuxban a Bourne shellt is a bash hajtja végre szűkített módban futva.
  18. Lásd programnyelv-generációk(en).
  19. Ha a program lefutott, de a szülő még nem kérdezte le a státusát, a program a memóriában marad. Az ilyen processzt nevezik zombinak(en).

Források[szerkesztés]

További információk[szerkesztés]

Kapcsolódó lapok[szerkesztés]