- Overview
- Text vs. Whatever
- Podwójne cudzysłowy wokół argumentów
- List Unique Species
- Rozwiązanie
- Why Record Commands in the History Before Running Them?
- Rozwiązanie
- Potok Nellego: Creating a Script
- Zmienne w skryptach powłoki
- Rozwiązanie
- Znajdź najdłuższy plik o podanym rozszerzeniu
- Rozwiązanie
- Scriptu Reading Comprehension
- Rozwiązania
- Debugowanie skryptów
- Rozwiązanie
- Kluczowe punkty
Overview
Nauczanie: 30 min
Ćwiczenia: 15 minPytania
Jak mogę zapisywać i ponownie używać poleceń?
Cele
Napisz skrypt powłoki, który uruchamia polecenie lub serię poleceń dla ustalonego zestawu plików.
Uruchom skrypt powłoki z wiersza poleceń.
Napisanie skryptu powłoki, który działa na zestawie plików zdefiniowanych przez użytkownika w wierszu poleceń.
Tworzenie potoków, które zawierają skrypty powłoki napisane przez Ciebie i innych.
Jesteśmy wreszcie gotowi, aby zobaczyć, co sprawia, że powłoka jest tak potężnym środowiskiem programowania.Zamierzamy wziąć polecenia, które często powtarzamy i zapisać je w plikach, tak byśmy mogli później ponownie wykonać wszystkie te operacje, wpisując jedno polecenie.Z powodów historycznych, zestaw poleceń zapisanych w pliku jest zwykle nazywany skryptem powłoki, ale nie popełnij błędu: są to w rzeczywistości małe programy.
Zacznijmy od powrotu do
molecules/i utworzenia nowego pliku,middle.sh, który będzie naszym skryptem powłoki:$ cd molecules$ nano middle.shPolecenie
nano middle.shotwiera plikmiddle.shw edytorze tekstowym 'nano’ (który działa w powłoce).Jeśli plik nie istnieje, zostanie utworzony. Możemy użyć edytora tekstowego do bezpośredniej edycji pliku – po prostu wstawimy następującą linię:head -n 15 octane.pdb | tail -n 5Jest to wariacja na temat potoku, który skonstruowaliśmy wcześniej: wybiera on linie 11-15 pliku
octane.pdb.Pamiętaj, że nie uruchamiamy go jeszcze jako polecenia: umieszczamy polecenia w pliku.Następnie zapisujemy plik (
Ctrl-Ow nano) i wychodzimy z edytora tekstu (Ctrl-Xw nano).Sprawdź, czy katalogmoleculeszawiera teraz plik o nazwiemiddle.sh.Po zapisaniu pliku możemy poprosić powłokę o wykonanie zawartych w nim poleceń.Nasza powłoka nazywa się
bash, więc wykonujemy następujące polecenie:$ 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.00Wystarczy, że dane wyjściowe naszego skryptu są dokładnie takie same, jakie otrzymalibyśmy, uruchamiając ten potok bezpośrednio.
Text vs. Whatever
Zazwyczaj nazywamy programy takie jak Microsoft Word lub LibreOffice Writer „edytorami tekstu”, ale musimy być nieco bardziej ostrożni, gdy chodzi o programowanie. Domyślnie Microsoft Word używa plików
.docxdo przechowywania nie tylko tekstu, ale także informacji o formatowaniu, takich jak czcionki, nagłówki, a wkrótce. Te dodatkowe informacje nie są przechowywane jako znaki i nic nie znaczą dla narzędzi takich jakhead: oczekują one, że pliki wejściowe będą zawierały tylko litery, cyfry i znaki interpunkcyjne występujące na standardowej klawiaturze komputerowej. Podczas edycji programów należy więc albo używać edytora tekstowego, albo uważać, by zapisywać pliki jako tekst jawny.A co, jeśli chcemy wybrać wiersze z dowolnego pliku? Moglibyśmy edytować
middle.shza każdym razem, by zmienić nazwę pliku, ale zajęłoby to prawdopodobnie więcej czasu niż ponowne wpisanie polecenia w powłoce i wykonanie go z nową nazwą pliku.Zamiast tego, edytujmymiddle.shi uczyńmy go bardziej uniwersalnym:$ nano middle.shTeraz, w „nano”, zastąp tekst
octane.pdbspecjalną zmienną zwaną:head -n 15 "" | tail -n 5Wewnątrz skryptu powłoki,
oznacza „pierwszą nazwę pliku (lub inny argument) w wierszu poleceń”.Możemy teraz uruchomić nasz skrypt w ten sposób:$ 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.00lub na innym pliku w ten sposób:
$ 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.00Podwójne cudzysłowy wokół argumentów
Z tego samego powodu, dla którego umieszczamy zmienną pętli w cudzysłowie, na wypadek gdyby nazwa pliku zawierała spacje, otaczamy
cudzysłowem.Obecnie, musimy edytować
middle.shza każdym razem, gdy chcemy dostosować zakres zwracanych linii. Naprawmy to, konfigurując nasz skrypt tak, aby zamiast tego używał trzech argumentów wiersza poleceń. Po pierwszym argumencie wiersza poleceń (), każdy dodatkowy argument, który podamy, będzie dostępny przez specjalne zmienne,,, które odnoszą się odpowiednio do pierwszego, drugiego i trzeciego argumentu wiersza poleceń.Wiedząc o tym, możemy użyć dodatkowych argumentów, aby określić zakres wierszy, które mają być przekazane odpowiednio do
headitail:$ nano middle.shhead -n "" "" | tail -n ""Możemy teraz uruchomić:
$ 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.00Zmieniając argumenty do naszego polecenia możemy zmienić zachowanie naszego skryptu:
$ 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 1To działa, ale następnej osobie, która przeczyta
middle.sh, może zająć chwilę zorientowanie się, co to robi.Możemy ulepszyć nasz skrypt, dodając na górze kilka komentarzy:$ nano middle.sh# Select lines from the middle of a file.# Usage: bash middle.sh filename end_line num_lineshead -n "" "" | tail -n ""Komentarz zaczyna się od znaku
#i biegnie do końca wiersza.Komputer ignoruje komentarze, ale są one nieocenione, aby pomóc ludziom (w tym Twojemu przyszłemu ja) zrozumieć i używać skryptów.Jedynym zastrzeżeniem jest to, że za każdym razem, gdy modyfikujesz skrypt, powinieneś sprawdzić, czy komentarz jest nadal dokładny: wyjaśnienie, które wysyła czytelnika w złym kierunku, jest gorsze niż jego brak.A co, jeśli chcemy przetworzyć wiele plików w jednym potoku?Na przykład, jeśli chcemy posortować nasze
.pdbpliki według długości, wpisalibyśmy:$ wc -l *.pdb | sort -nponieważ
wc -lpodaje liczbę linii w plikach (przypomnijmy, żewcoznacza 'word count’, dodanie opcji-loznacza 'count lines’ zamiast tego), asort -nsortuje rzeczy numerycznie.Moglibyśmy umieścić to w pliku, ale wtedy sortowałoby to tylko listę.pdbplików w bieżącym katalogu.Jeśli chcemy być w stanie uzyskać posortowaną listę innych rodzajów plików, potrzebujemy sposobu na wprowadzenie tych wszystkich nazw do skryptu. Nie możemy użyć,i tak dalej, ponieważ nie wiemy ile jest plików. Zamiast tego, używamy specjalnej zmiennej$@, która oznacza, 'Wszystkie argumenty wiersza poleceń do skryptu powłoki’.Powinniśmy również umieścić$@wewnątrz cudzysłowów, by obsłużyć przypadek argumentów zawierających spacje ("$@"jest specjalną składnią i odpowiada""""…).Oto przykład:
$ 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 ma kilkaset plików danych, z których każdy jest sformatowany w ten sposób:
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,1Przykład tego typu pliku jest podany w
data-shell/data/animal-counts/animals.txt.Możemy użyć polecenia
cut -d , -f 2 animals.txt | sort | uniqdo uzyskania unikalnych gatunków wanimals.txt. Aby uniknąć konieczności każdorazowego wpisywania tej serii poleceń, naukowiec może zamiast tego napisać skrypt powłoki.Napisz skrypt powłoki o nazwie
species.sh, który przyjmuje dowolną liczbę nazw roślin jako argumenty wiersza poleceń i używa wariantu powyższego polecenia do wypisania listy unikalnych gatunków występujących w każdym z tych plików osobno.Rozwiązanie
# 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 | uniqdoneZałóżmy, że właśnie wykonaliśmy serię poleceń, które zrobiły coś pożytecznego – na przykład utworzyły wykres, który chcielibyśmy wykorzystać w pracy. Chcielibyśmy móc odtworzyć ten wykres później, jeśli zajdzie taka potrzeba, więc chcemy zapisać polecenia w pliku.Zamiast wpisywać je ponownie (i potencjalnie się mylić) możemy zrobić tak:
$ history | tail -n 5 > redo-figure-3.shPlik
redo-figure-3.shzawiera teraz: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.shPo chwili pracy w edytorze nad usunięciem numerów seryjnych na komendach,oraz usunięciu ostatniej linii,w której wywołaliśmy komendę
history,mamy całkowicie dokładny zapis tego,jak stworzyliśmy tę figurę.Why Record Commands in the History Before Running Them?
Jeśli uruchomisz polecenie:
$ history | tail -n 5 > recent.shostatnim poleceniem w pliku jest samo polecenie
history, tzn,powłoka dodałahistorydo dziennika poleceń przed jego faktycznym wykonaniem. W rzeczywistości, powłoka zawsze dodaje polecenia do dziennika przed ich uruchomieniem. Jak sądzisz, dlaczego to robi?Rozwiązanie
Jeśli polecenie powoduje awarię lub zawieszenie się czegoś, może być użyteczne wiedzieć, jakie to było polecenie, w celu zbadania problemu.Gdyby polecenie było zapisywane tylko po jego uruchomieniu, nie mielibyśmy zapisu ostatniego uruchomionego polecenia w przypadku awarii.
W praktyce, większość ludzi tworzy skrypty powłoki, uruchamiając polecenia w znaku zachęty powłoki kilka razy, aby upewnić się, że robią to dobrze, a następnie zapisując je w pliku do ponownego użycia.Ten styl pracy pozwala ludziom na recykling tego, co odkrywają o swoich danych i przepływie pracy za pomocą jednego wywołania
historyi odrobiny edycji, aby oczyścić dane wyjściowe i zapisać je jako skrypt powłoki.Potok Nellego: Creating a Script
Przełożony Nelle nalegał, aby wszystkie jej analizy były powtarzalne. Najłatwiejszym sposobem na uchwycenie wszystkich kroków jest skrypt.
Najpierw wracamy do katalogu z danymi Nelle:
$ cd ../north-pacific-gyre/2012-07-03/Uruchamia edytor i pisze następującą treść:
# Calculate stats for data files.for datafile in "$@"do echo $datafile bash goostats $datafile stats-$datafiledoneZapisuje to w pliku o nazwie
do-stats.sh, dzięki czemu może teraz powtórzyć pierwszy etap analizy, wpisując:$ bash do-stats.sh NENE*.txtMoże też zrobić tak:
$ bash do-stats.sh NENE*.txt | wc -lżeby na wyjściu była tylko liczba przetworzonych plików, a nie nazwy plików, które zostały przetworzone.
Jedną z rzeczy, na które należy zwrócić uwagę w skrypcie Nelle, jest to, że pozwala on osobie go uruchamiającej zdecydować, jakie pliki mają być przetwarzane.Mogła go napisać jako:
# Calculate stats for Site A and Site B data files.for datafile in NENE*.txtdo echo $datafile bash goostats $datafile stats-$datafiledoneZaletą jest to, że zawsze wybiera właściwe pliki: nie musi pamiętać o wykluczeniu plików 'Z’. Wadą jest to, że zawsze wybiera tylko te pliki – nie może go uruchomić na wszystkich plikach (łącznie z 'Z’), ani na plikach 'G’ lub 'H’, które produkują jej koledzy na Antarktydzie, bez edycji skryptu.Jeśli chciałaby być bardziej odważna, mogłaby zmodyfikować skrypt tak, aby sprawdzał argumenty wiersza poleceń i używał
NENE*.txt, jeśli żadne nie zostały podane.Oczywiście, wprowadza to kolejny kompromis między elastycznością a złożonością.Zmienne w skryptach powłoki
W katalogu
moleculeswyobraź sobie, że masz skrypt powłoki o nazwiescript.shzawierający następujące polecenia:head -n tail -nPrzebywając w katalogu
molecules, wpisujemy następujące polecenie:bash script.sh '*.pdb' 1 1Którego z następujących wyjść można się spodziewać?
- Wszystkie wiersze pomiędzy pierwszym i ostatnim wierszem każdego pliku kończącego się na
.pdbw katalogumolecules- Pierwszy i ostatni wiersz każdego pliku kończącego się na
.pdbw katalogumoleculeskatalog- Pierwszy i ostatni wiersz każdego pliku w katalogu
molecules- Błąd z powodu cudzysłowów wokół
*.pdbRozwiązanie
Prawidłowa odpowiedź to 2.
Zmienne specjalne $1, $2 i $3 reprezentują argumenty wiersza poleceń przekazane skryptowi, tak że uruchamiane są następujące polecenia:
$ 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.pdbPowłoka nie rozwija argumentu
'*.pdb', ponieważ jest on ujęty w cudzysłów. W związku z tym pierwszym argumentem skryptu jest'*.pdb', który zostaje rozwinięty w skrypcie przezheaditail.Znajdź najdłuższy plik o podanym rozszerzeniu
Napisz skrypt powłoki o nazwie
longest.sh, który jako argumenty przyjmuje nazwę katalogu i rozszerzenie nazwy pliku, a następnie wypisuje nazwę pliku z największą liczbą wierszy w tym katalogu o tym rozszerzeniu. Na przykład:$ bash longest.sh /tmp/data pdbwypisuje nazwę pliku
.pdbw katalogu/tmp/data, który ma najwięcej linii.Rozwiązanie
# 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 1Pierwsza część potoku,
wc -l /*. | sort -n, liczy wiersze w każdym pliku i sortuje je numerycznie (największe jako ostatnie). Gdy jest więcej niż jeden plik,wcwypisuje również końcowy wiersz podsumowujący, podający całkowitą liczbę linii we wszystkich plikach. Użyjemytail-n 2 | head -n 1, aby wyrzucić ten ostatni wiersz.Za pomocą
wc -l /*. | sort -n | tail -n 1zobaczymy końcowy wiersz podsumowujący: możemy budować nasz potok po kawałku, aby mieć pewność, że rozumiemy dane wyjściowe.Scriptu Reading Comprehension
Dla tego pytania rozważ jeszcze raz katalog
data-shell/molecules.Zawiera on pewną liczbę plików.pdboprócz wszelkich innych plików, które mogłeś utworzyć.Wyjaśnij, co zrobiłby każdy z trzech poniższych skryptów po uruchomieniu odpowiednio jakobash script1.sh *.pdb,bash script2.sh *.pdbibash script3.sh *.pdb.# Script 1echo *.*# Script 2for filename in do cat $filenamedone# Script 3echo [email protected]Rozwiązania
W każdym przypadku powłoka interpretuje symbol wieloznaczny w
*.pdbprzed przekazaniem wynikowej listy nazw plików jako argumentów skryptu.Skrypt 1 wypisałby listę wszystkich plików zawierających w swojej nazwie kropkę.Argumenty przekazane do skryptu nie są w rzeczywistości nigdzie w nim użyte.
Skrypt 2 wypisałby zawartość pierwszych 3 plików z rozszerzeniem
.pdb.,iodnoszą się odpowiednio do pierwszego, drugiego i trzeciego argumentu.Skrypt 3 wypisałby wszystkie argumenty skryptu (tj. wszystkie pliki
.pdb), po których następuje.pdb.$@odnosi się do wszystkich argumentów przekazanych skryptowi powłoki.cubane.pdb ethane.pdb methane.pdb octane.pdb pentane.pdb propane.pdb.pdbDebugowanie skryptów
Załóżmy, że zapisałeś następujący skrypt w pliku o nazwie
do-errors.shw katalogunorth-pacific-gyre/2012-07-03Nelle:# Calculate stats for data files.for datafile in "$@"do echo $datfile bash goostats $datafile stats-$datafiledonePo jego uruchomieniu:
$ bash do-errors.sh NENE*.txtwynik jest pusty.Aby dowiedzieć się dlaczego, uruchom ponownie skrypt, używając opcji
-x:bash -x do-errors.sh NENE*.txtCo pokazuje wyjście? Który wiersz jest odpowiedzialny za błąd?
Rozwiązanie
Opcja
-xpowoduje, że programbashdziała w trybie debugowania. Powoduje to wypisanie każdego polecenia w trakcie jego wykonywania, co pomaga zlokalizować błędy. W tym przykładzie widzimy, żeechonic nie wypisuje. Zrobiliśmy literówkę w nazwie zmiennej pętli, a zmiennadatfilenie istnieje, dlatego zwraca pusty łańcuch.Kluczowe punkty
Zapisywanie poleceń w plikach (zwykle nazywanych skryptami powłoki) w celu ponownego użycia.
bashuruchamia polecenia zapisane w pliku.
$@odnosi się do wszystkich argumentów wiersza poleceń skryptu powłoki.
,, itd, odnoszą się do pierwszego argumentu wiersza poleceń, drugiego argumentu wiersza poleceń itd.Umieszczaj zmienne w cudzysłowach, jeśli wartości mogą zawierać spacje.
Pozwalanie użytkownikom na decydowanie, jakie pliki mają być przetwarzane, jest bardziej elastyczne i spójne z wbudowanymi poleceniami uniksowymi.
.