- Áttekintés
- Text vs. akármi
- Dupla idézőjelek az argumentumok körül
- List Unique Species
- megoldás
- Miért rögzítjük a parancsokat az előzményekben, mielőtt lefuttatjuk őket?
- megoldás
- 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.
- Változók a Shell szkriptekben
- megoldás
- A leghosszabb fájl keresése adott kiterjesztéssel
- megoldás
- Script olvasásértés
- Solutions
- Skriptek hibakeresése
- megoldás
- Kulcspontok
Á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, amiddle.sh-at, amely a shell scriptünk lesz:$ cd molecules$ nano middle.shA
nano middle.shparancs megnyitja amiddle.shfá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 5Ez a korábban szerkesztett pipának egy változata:a
octane.pdbfá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-Oa nano-ban), és kilépünk a szövegszerkesztőből (Ctrl-Xa nano-ban).Ellenőrizzük, hogy amoleculeskönyvtárban most már van egymiddle.shnevű 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.shATOM 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.00Egyé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
.docxfá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 ahead: 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.shparancsot 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 amiddle.sh-t, és tegyük sokoldalúbbá:$ nano middle.shMost a “nano”-ban a
octane.pdbszöveget helyettesítsük anevű speciális változóval:head -n 15 "" | tail -n 5Egy 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.pdbATOM 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.00vagy egy másik fájlon így:
$ bash middle.sh pentane.pdbATOM 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.00Dupla 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 atailszámára átadandó sorok tartományát:$ nano middle.shhead -n "" "" | tail -n ""Most futtathatjuk:
$ bash middle.sh pentane.pdb 15 5ATOM 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.00A parancsunk argumentumainak megváltoztatásával megváltoztathatjuk a szkriptünk viselkedését:
$ bash middle.sh pentane.pdb 20 5ATOM 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 1Ez működik,de a
middle.shkö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
.pdbfájljainkat hossz szerint akarjuk rendezni, akkor beírjuk:$ wc -l *.pdb | sort -nmert a
wc -la fájlok sorainak számát sorolja fel(emlékezzünk, hogy awca “szószámot” jelenti, a-lopció hozzáadása a “sorok számát” jelenti helyette)és asort -nnumerikusan rendezi a dolgokat.Ezt beletehetnénk egy fájlba,de akkor mindig csak az aktuális könyvtárban lévő.pdbfá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/*.dat9 methane.pdb12 ethane.pdb15 propane.pdb20 cubane.pdb21 pentane.pdb30 octane.pdb163 ../creatures/basilisk.dat163 ../creatures/minotaur.dat163 ../creatures/unicorn.dat596 totalList 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,1Egy 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 | uniqparanccsal előállíthatjuk aanimals.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.shnevű 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 | uniqdoneTegyü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.shA
redo-figure-3.shfá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.shMiután egy szerkesztőprogramban egy pillanatra eltávolítjuk a sorszámokat a parancsokból,és az utolsó sort,ahol a
historyparancsot 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.shA fájlban az utolsó parancs maga a
historyparancs, azaz,a héj hozzáadta ahistoryparancsot 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-$datafiledoneEzt 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*.txtEzt 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-$datafiledoneAz 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
moleculeskönyvtárban képzeljük el, hogy van egyscript.shnevű Shell szkriptünk, amely a következő parancsokat tartalmazza:head -n tail -nAmíg a
moleculeskönyvtárban van, beírja a következő parancsot:bash script.sh '*.pdb' 1 1Az alábbi kimenetek közül melyikre számítana?
- A
moleculeskönyvtárban- A
moleculeskönyvtárban.pdb-re végződő minden egyes fájl első és utolsó sora közötti sorokat- A
moleculeskönyvtárban.pdb-re végződő minden egyes fájl első és utolsó sorát. könyvtár- A
moleculeskönyvtárban- A
moleculeskönyvtárban található minden egyes fájl első és utolsó sora- Hiba a
*.pdbmegoldá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.pdbA shell nem bontja ki a
'*.pdb'-et, mert idézőjelek közé van zárva.Így a script első argumentuma'*.pdb', amely a scriptbenheadéstail-vel bővül ki.A leghosszabb fájl keresése adott kiterjesztéssel
Írd meg a
longest.shnevű 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 pdba
/tmp/data-ben található.pdbfá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 1A csővezeték első része,
wc -l /*. | sort -nmegszámolja az egyes fájlok sorait, és numerikusan rendezi őket (a legnagyobbat utoljára). Ha egynél több fájl van, awcegy végső összefoglaló sort is kiad, amely megadja az összes fájl összes sorának számát. Atail-n 2 | head -n 1segítségével kidobjuk ezt az utolsó sort.A
wc -l /*. | sort -n | tail -n 1segí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/moleculeskönyvtárat nézzük.Ez számos.pdbfá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, habash script1.sh *.pdb,bash script2.sh *.pdb, illetvebash script3.sh *.pdbné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
.pdbkiterjesztésű fájl tartalmát írná ki.A,ésaz első, a második és a harmadik argumentumra utal.A 3. script kiírná a script összes argumentumát (azaz az összes
.pdbfájlt),amelyet a.pdbkövet.A$@a shell scriptnek adott összes argumentumra utal.cubane.pdb ethane.pdb methane.pdb octane.pdb pentane.pdb propane.pdb.pdbSkriptek hibakeresése
Tegyük fel, hogy a következő szkriptet egy
do-errors.shnevű fájlba mentettük Nellenorth-pacific-gyre/2012-07-03könyvtárában:# Calculate stats for data files.for datafile in "$@"do echo $datfile bash goostats $datafile stats-$datafiledoneA futtatásakor:
$ bash do-errors.sh NENE*.txta kimenet üres.Hogy kiderítse, miért, futtassa újra a szkriptet a
-xopcióval:bash -x do-errors.sh NENE*.txtMit mutat a kimenet?Melyik sor felelős a hibáért?
megoldás
A
-xopció hatására abashhibakeresé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 aechonem ír ki semmit. Elgépeltük a ciklusváltozó nevét, és adatfilevá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.
basha 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.