Shell szkriptek

okt 29, 2021

Áttekintés

Tanítás: 30 perc
GYakorlatok:

Célok

  • Írjon egy shell scriptet, amely egy parancsot vagy parancssorozatot futtat egy meghatározott fájlkészletre.

  • Futtasson egy shell scriptet a parancssorból.

  • Egy olyan héjszkript írása, amely a felhasználó által a parancssoron meghatározott fájlkészleten dolgozik.

  • Pipeline-ok készítése, amelyek az ön és mások által írt héjszkripteket tartalmazzák.

Végre láthatjuk, mitől olyan hatékony programozási környezet a shell.Fogjuk azokat a parancsokat, amelyeket gyakran ismétlünk, és fájlba mentjük őket,hogy később egyetlen parancs beírásával újra lefuttathassuk ezeket a műveleteket.Történelmi okokból a fájlba mentett parancsok halmazát általában shell scriptnek nevezik,de ne tévedjünk:ezek valójában kis programok.

Kezdjük azzal, hogy visszamegyünk a molecules/-be, és létrehozunk egy új fájlt, a middle.sh-at, amely a shell scriptünk lesz:

$ cd molecules$ nano middle.sh

A nano middle.sh parancs megnyitja a middle.sh fájlt a ‘nano’ szövegszerkesztőben (amely a shellben fut).Ha a fájl nem létezik, akkor létrejön.a szövegszerkesztővel közvetlenül is szerkeszthetjük a fájlt – egyszerűen beillesztjük a következő sort:

head -n 15 octane.pdb | tail -n 5

Ez a korábban szerkesztett pipának egy változata:a octane.pdb fájl 11-15. sorát választja ki.Ne feledjük, hogy még nem parancsként futtatjuk:a parancsokat egy fájlba helyezzük.

Ezután elmentjük a fájlt (Ctrl-O a nano-ban), és kilépünk a szövegszerkesztőből (Ctrl-X a nano-ban).Ellenőrizzük, hogy a molecules könyvtárban most már van egy middle.sh nevű fájl.

Miután elmentettük a fájlt,megkérhetjük a héjat, hogy hajtsa végre a benne lévő parancsokat.a mi héjunk neve bash, így a következő parancsot futtatjuk:

$ bash middle.sh
ATOM 9 H 1 -4.502 0.681 0.785 1.00 0.00ATOM 10 H 1 -5.254 -0.243 -0.537 1.00 0.00ATOM 11 H 1 -4.357 1.252 -0.895 1.00 0.00ATOM 12 H 1 -3.009 -0.741 -1.467 1.00 0.00ATOM 13 H 1 -3.172 -1.337 0.206 1.00 0.00

Egyértelmű, hogy a szkriptünk kimenete pontosan az, amit akkor kapnánk, ha közvetlenül futtatnánk ezt a csővezetéket.

Text vs. akármi

Az olyan programokat, mint a Microsoft Word vagy a LibreOffice Writer általában “szövegszerkesztőnek” nevezzük, de a programozásnál kicsit óvatosabbnak kell lennünk. A Microsoft Word alapértelmezés szerint .docx fájlokat használ nemcsak a szöveg, hanem a betűtípusokra, címsorokra és hamarosan a formázási adatok tárolására is. Ezek az extra információk nem karakterek formájában tárolódnak, és nem jelentenek semmit az olyan eszközök számára, mint a head: ezek azt várják, hogy a bemeneti fájlok csak a szabványos számítógépes billentyűzet betűit, számjegyeit és írásjeleit tartalmazzák. A programok szerkesztésekor ezért vagy egyszerű szövegszerkesztőt kell használnunk, vagy ügyelnünk kell arra, hogy a fájlokat egyszerű szövegként mentsük el.

Mi van, ha egy tetszőleges fájlból akarunk sorokat kiválasztani?Minden egyes alkalommal szerkeszthetjük a middle.sh parancsot a fájlnév megváltoztatására,de ez valószínűleg tovább tartana, mintha a parancsot újra begépelnénk a shellben, és új fájlnévvel hajtanánk végre.Ehelyett szerkesszük meg a middle.sh-t, és tegyük sokoldalúbbá:

$ nano middle.sh

Most a “nano”-ban a octane.pdb szöveget helyettesítsük a nevű speciális változóval:

head -n 15 "" | tail -n 5

Egy héjszkriptben a jelentése “az első fájlnév (vagy más argumentum) a parancssorban”.Most már így futtathatjuk a szkriptünket:

$ bash middle.sh octane.pdb
ATOM 9 H 1 -4.502 0.681 0.785 1.00 0.00ATOM 10 H 1 -5.254 -0.243 -0.537 1.00 0.00ATOM 11 H 1 -4.357 1.252 -0.895 1.00 0.00ATOM 12 H 1 -3.009 -0.741 -1.467 1.00 0.00ATOM 13 H 1 -3.172 -1.337 0.206 1.00 0.00

vagy egy másik fájlon így:

$ bash middle.sh pentane.pdb
ATOM 9 H 1 1.324 0.350 -1.332 1.00 0.00ATOM 10 H 1 1.271 1.378 0.122 1.00 0.00ATOM 11 H 1 -0.074 -0.384 1.288 1.00 0.00ATOM 12 H 1 -0.048 -1.362 -0.205 1.00 0.00ATOM 13 H 1 -1.183 0.500 -1.412 1.00 0.00

Dupla idézőjelek az argumentumok körül

Azért, amiért a ciklusváltozót is dupla idézőjelek közé tesszük, ha a fájlnév véletlenül szóközöket tartalmaz, a -t is dupla idézőjelekkel vesszük körül.

Pillanatnyilag a middle.sh-t kell szerkesztenünk minden alkalommal, amikor módosítani akarjuk a visszaadott sorok tartományát. Oldjuk meg ezt úgy, hogy a szkriptünket úgy állítjuk be, hogy ehelyett három parancssori argumentumot használjon. Az első parancssori argumentum () után minden további általunk megadott argumentum a , , speciális változókon keresztül lesz elérhető, amelyek az első, második, illetve harmadik parancssori argumentumra utalnak.

Azzal, hogy ezt tudjuk, a további argumentumokkal meghatározhatjuk a head, illetve a tail számára átadandó sorok tartományát:

$ nano middle.sh
head -n "" "" | tail -n ""

Most futtathatjuk:

$ bash middle.sh pentane.pdb 15 5
ATOM 9 H 1 1.324 0.350 -1.332 1.00 0.00ATOM 10 H 1 1.271 1.378 0.122 1.00 0.00ATOM 11 H 1 -0.074 -0.384 1.288 1.00 0.00ATOM 12 H 1 -0.048 -1.362 -0.205 1.00 0.00ATOM 13 H 1 -1.183 0.500 -1.412 1.00 0.00

A parancsunk argumentumainak megváltoztatásával megváltoztathatjuk a szkriptünk viselkedését:

$ bash middle.sh pentane.pdb 20 5
ATOM 14 H 1 -1.259 1.420 0.112 1.00 0.00ATOM 15 H 1 -2.608 -0.407 1.130 1.00 0.00ATOM 16 H 1 -2.540 -1.303 -0.404 1.00 0.00ATOM 17 H 1 -3.393 0.254 -0.321 1.00 0.00TER 18 1

Ez működik,de a middle.sh következő olvasónak egy pillanatba telhet, mire rájön, mit csinál.Javíthatunk a szkriptünkön, ha a tetejére néhány megjegyzést teszünk:

$ nano middle.sh
# Select lines from the middle of a file.# Usage: bash middle.sh filename end_line num_lineshead -n "" "" | tail -n ""

A megjegyzés egy # karakterrel kezdődik, és a sor végéig tart.A számítógép figyelmen kívül hagyja a megjegyzéseket,de felbecsülhetetlen értékűek, mert segítenek az embereknek (beleértve a leendő önmagadat is) megérteni és használni a szkripteket.Az egyetlen kikötés, hogy minden egyes alkalommal, amikor módosítod a szkriptet,ellenőrizd, hogy a megjegyzés még mindig pontos-e:egy olyan magyarázat, amely rossz irányba tereli az olvasót, rosszabb, mintha egyáltalán nem lenne.

Mi van, ha sok fájlt akarunk feldolgozni egyetlen csővezetékben?Ha például a .pdb fájljainkat hossz szerint akarjuk rendezni, akkor beírjuk:

$ wc -l *.pdb | sort -n

mert a wc -l a fájlok sorainak számát sorolja fel(emlékezzünk, hogy a wc a “szószámot” jelenti, a -l opció hozzáadása a “sorok számát” jelenti helyette)és a sort -n numerikusan rendezi a dolgokat.Ezt beletehetnénk egy fájlba,de akkor mindig csak az aktuális könyvtárban lévő .pdb fájlok listáját rendezné.Ha azt akarjuk, hogy másfajta fájlok rendezett listáját is megkaphassuk,valahogyan be kell juttatnunk az összes nevet a szkriptbe.Nem használhatjuk a , és így tovább,mert nem tudjuk, hogy hány fájl van.Ehelyett a $@ speciális változót használjuk,ami azt jelenti,’All of the command-line arguments to the shell script’.A $@-t is dupla idézőjelek közé kell tennünk, hogy kezeljük a szóközöket tartalmazó argumentumok esetét("$@" speciális szintaxis, és egyenértékű a "" "" …).

Íme egy példa:

$ nano sorted.sh
# Sort files by their length.# Usage: bash sorted.sh one_or_more_filenameswc -l "$@" | sort -n
$ bash sorted.sh *.pdb ../creatures/*.dat
9 methane.pdb12 ethane.pdb15 propane.pdb20 cubane.pdb21 pentane.pdb30 octane.pdb163 ../creatures/basilisk.dat163 ../creatures/minotaur.dat163 ../creatures/unicorn.dat596 total

List Unique Species

Leah-nak több száz adatállománya van, mindegyik így van formázva:

2013-11-05,deer,52013-11-05,rabbit,222013-11-05,raccoon,72013-11-06,rabbit,192013-11-06,deer,22013-11-06,fox,12013-11-07,rabbit,182013-11-07,bear,1

Egy példa az ilyen típusú fájlra a data-shell/data/animal-counts/animals.txt-ban található.

A cut -d , -f 2 animals.txt | sort | uniq paranccsal előállíthatjuk a animals.txt-ben szereplő egyedi fajokat. Annak érdekében, hogy ne kelljen minden alkalommal begépelni ezt a parancssort, a tudós írhat helyette egy shell scriptet.

Írd meg a species.sh nevű shell scriptet, amely tetszőleges számú fájlnevet fogad el parancssori argumentumként, és a fenti parancs egy variációját használja a fenti fájlok mindegyikében külön-külön előforduló egyedi fajok listájának kiírására.

megoldás

# Script to find unique species in csv files where species is the second data field# This script accepts any number of file names as command line arguments# Loop over all filesfor file in $@doecho "Unique species in $file:"# Extract species namescut -d , -f 2 $file | sort | uniqdone

Tegyük fel, hogy most futtattunk le egy sor parancsot, amelyek valami hasznosat csináltak – például létrehoztak egy grafikont, amelyet egy dolgozatban szeretnénk használni.Szeretnénk később újra létrehozni a grafikont, ha szükséges,ezért a parancsokat egy fájlba akarjuk menteni.Ahelyett, hogy újra beírnánk őket(és esetleg elrontanánk)ezt tehetjük:

$ history | tail -n 5 > redo-figure-3.sh

A redo-figure-3.sh fájl most már tartalmazza:

297 bash goostats NENE01729B.txt stats-NENE01729B.txt298 bash goodiff stats-NENE01729B.txt /data/validated/01729.txt > 01729-differences.txt299 cut -d ',' -f 2-3 01729-differences.txt > 01729-time-series.txt300 ygraph --format scatter --color bw --borders none 01729-time-series.txt figure-3.png301 history | tail -n 5 > redo-figure-3.sh

Miután egy szerkesztőprogramban egy pillanatra eltávolítjuk a sorszámokat a parancsokból,és az utolsó sort,ahol a history parancsot hívtuk,teljesen pontos feljegyzést kapunk arról,hogyan hoztuk létre ezt az ábrát.

Miért rögzítjük a parancsokat az előzményekben, mielőtt lefuttatjuk őket?

Ha a következő parancsot futtatjuk:

$ history | tail -n 5 > recent.sh

A fájlban az utolsó parancs maga a history parancs, azaz,a héj hozzáadta a history parancsot a parancsnaplóhoz, mielőtt ténylegesen lefuttatta volna. Valójában a shell mindig hozzáadja a parancsokat a naplóhoz, mielőtt lefuttatná őket. Mit gondolsz, miért csinálja ezt?

megoldás

Ha egy parancs valaminek az összeomlását vagy leállását okozza, hasznos lehet tudni, hogy mi volt az a parancs, hogy kivizsgálhassuk a problémát.Ha a parancsot csak a futtatás után rögzítenénk, akkor összeomlás esetén nem rendelkeznénk az utolsó futtatott parancsról.

A gyakorlatban a legtöbb ember úgy fejleszt shell szkripteket, hogy néhányszor lefuttat parancsokat a shell prompton, hogy megbizonyosodjon arról, hogy a megfelelő dolgot csinálják, majd elmenti őket egy fájlba, hogy újra felhasználhassa őket.Ez a munkastílus lehetővé teszi, hogy az emberek újrahasznosítsák mindazt, amit felfedeznek az adataikról és a munkafolyamatukról, egyetlen hívással a historyhoz és egy kis szerkesztéssel, hogy megtisztítsák a kimenetet és shell szkriptként elmentsék.

Nelle’s Pipeline: Nelle felettese ragaszkodott ahhoz, hogy minden analitikájának reprodukálhatónak kell lennie. Az összes lépés rögzítésének legegyszerűbb módja egy szkript.

Először térjünk vissza Nelle adatkönyvtárába:

$ cd ../north-pacific-gyre/2012-07-03/

Futtatja a szerkesztőt, és a következőket írja:

# Calculate stats for data files.for datafile in "$@"do echo $datafile bash goostats $datafile stats-$datafiledone

Ezt egy do-stats.shnevű fájlba menti, így most az elemzés első szakaszát újra elvégezheti a beírással:

$ bash do-stats.sh NENE*.txt

Ezt is megteheti:

$ bash do-stats.sh NENE*.txt | wc -l

így a kimenet csak a feldolgozott fájlok száma lesz, nem pedig a feldolgozott fájlok neve.

Egy dolog, amit meg kell jegyezni Nelle szkriptjével kapcsolatban, hogy a futtató személy döntheti el, hogy milyen fájlokat dolgozzon fel.Írhatta volna így is:

# Calculate stats for Site A and Site B data files.for datafile in NENE*.txtdo echo $datafile bash goostats $datafile stats-$datafiledone

Az előnye az, hogy ez mindig a megfelelő fájlokat választja ki:nem kell emlékeznie arra, hogy kizárja a “Z” fájlokat.A hátránya az, hogy mindig csak ezeket a fájlokat választja ki – nem tudja futtatni az összes fájlra (beleértve a “Z” fájlokat is),vagy a “G” vagy “H” fájlokra, amelyeket az antarktiszi kollégái készítenek,a szkript szerkesztése nélkül.Ha kalandvágyóbb akar lenni, módosíthatja a szkriptet, hogy ellenőrizze a parancssori argumentumokat, és a NENE*.txt-t használja, ha nincs megadva.Természetesen ez újabb kompromisszumot jelent a rugalmasság és a bonyolultság között.

Változók a Shell szkriptekben

A molecules könyvtárban képzeljük el, hogy van egy script.sh nevű Shell szkriptünk, amely a következő parancsokat tartalmazza:

head -n  tail -n  

Amíg a molecules könyvtárban van, beírja a következő parancsot:

bash script.sh '*.pdb' 1 1

Az alábbi kimenetek közül melyikre számítana?

  1. A molecules könyvtárban
  2. A molecules könyvtárban .pdb-re végződő minden egyes fájl első és utolsó sora közötti sorokat
  3. A molecules könyvtárban .pdb-re végződő minden egyes fájl első és utolsó sorát. könyvtár
  4. A molecules könyvtárban
  5. A molecules könyvtárban található minden egyes fájl első és utolsó sora
  6. Hiba a *.pdb

megoldás

A helyes válasz: 2.

A $1, $2 és $3 speciális változók a scriptnek adott parancssori argumentumokat jelentik, így a futtatott parancsok a következők:

$ head -n 1 cubane.pdb ethane.pdb octane.pdb pentane.pdb propane.pdb$ tail -n 1 cubane.pdb ethane.pdb octane.pdb pentane.pdb propane.pdb

A shell nem bontja ki a '*.pdb'-et, mert idézőjelek közé van zárva.Így a script első argumentuma '*.pdb', amely a scriptben head és tail-vel bővül ki.

A leghosszabb fájl keresése adott kiterjesztéssel

Írd meg a longest.sh nevű héjszkriptet, amely egy könyvtár nevét és egy fájlnév kiterjesztését veszi argumentumként, és kiírja annak a fájlnak a nevét, amely a legtöbb sort tartalmazza az adott könyvtárban az adott kiterjesztéssel. Például:

$ bash longest.sh /tmp/data pdb

a /tmp/data-ben található .pdb fájl nevét írja ki, amelyik a legtöbb sort tartalmazza.

megoldás

# Shell script which takes two arguments:# 1. a directory name# 2. a file extension# and prints the name of the file in that directory# with the most lines which matches the file extension.wc -l /*. | sort -n | tail -n 2 | head -n 1

A csővezeték első része, wc -l /*. | sort -n megszámolja az egyes fájlok sorait, és numerikusan rendezi őket (a legnagyobbat utoljára). Ha egynél több fájl van, a wc egy végső összefoglaló sort is kiad, amely megadja az összes fájl összes sorának számát. A tail-n 2 | head -n 1 segítségével kidobjuk ezt az utolsó sort.

A wc -l /*. | sort -n | tail -n 1 segítségével megnézzük a végső összefoglaló sort: a csővezetékünket darabokra bontva építhetjük fel, hogy biztosan megértsük a kimenetet.

Script olvasásértés

Ezzel a kérdéssel kapcsolatban ismét a data-shell/molecules könyvtárat nézzük.Ez számos .pdb fájlt tartalmaz az esetlegesen létrehozott egyéb fájlokon kívül.Magyarázza meg, hogy az alábbi három szkript mindegyike mit csinálna, ha bash script1.sh *.pdb, bash script2.sh *.pdb, illetve bash script3.sh *.pdb néven futna.

# Script 1echo *.*
# Script 2for filename in   do cat $filenamedone
# Script 3echo [email protected]

Solutions

A shell minden esetben kibontja a *.pdb-ben lévő jokert, mielőtt az így kapott fájlnevek listáját argumentumként átadja a szkriptnek.

Az 1. szkript kiírná az összes olyan fájl listáját, amelynek nevében pont van.A szkriptnek átadott argumentumok valójában nem kerülnek felhasználásra a szkriptben.

A 2. szkript az első 3 .pdb kiterjesztésű fájl tartalmát írná ki.A , és az első, a második és a harmadik argumentumra utal.

A 3. script kiírná a script összes argumentumát (azaz az összes .pdb fájlt),amelyet a .pdb követ.A $@ a shell scriptnek adott összes argumentumra utal.

cubane.pdb ethane.pdb methane.pdb octane.pdb pentane.pdb propane.pdb.pdb

Skriptek hibakeresése

Tegyük fel, hogy a következő szkriptet egy do-errors.shnevű fájlba mentettük Nelle north-pacific-gyre/2012-07-03 könyvtárában:

# Calculate stats for data files.for datafile in "$@"do echo $datfile bash goostats $datafile stats-$datafiledone

A futtatásakor:

$ bash do-errors.sh NENE*.txt

a kimenet üres.Hogy kiderítse, miért, futtassa újra a szkriptet a -x opcióval:

bash -x do-errors.sh NENE*.txt

Mit mutat a kimenet?Melyik sor felelős a hibáért?

megoldás

A -x opció hatására a bash hibakeresési módban fut.Ez minden egyes parancsot kiír, ahogyan az fut, ami segít a hibák felkutatásában.Ebben a példában láthatjuk, hogy a echo nem ír ki semmit. Elgépeltük a ciklusváltozó nevét, és a datfile változó nem létezik, ezért üres karakterláncot ad vissza.

Kulcspontok

  • A parancsokat fájlokba (általában héjszkripteknek nevezik) mentjük, hogy újra felhasználhassuk őket.

  • bash a fájlba mentett parancsokat futtatja.

  • $@ egy héjszkript összes parancssori argumentumára utal.

  • , stb, az első parancssori argumentumra, a második parancssori argumentumra stb. utal.

  • A változókat idézőjelbe tesszük, ha az értékek szóközöket tartalmazhatnak.

  • Az, hogy a felhasználók dönthetik el, milyen fájlokat dolgozzanak fel, rugalmasabb és következetesebb a beépített Unix parancsokkal.

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.