6 Efektivno pisanje Bash skripti
Efikasna dijagnostika sistema i automatizacija rutinskih zadataka zahtevaju razumevanje načina na koji se komandna linija i alati koriste za rešavanje problema. Ovo poglavlje prikazuje jednostavne i napredne tehnike Bash skriptovanja.
6.1 Zaglavlje
Zaglavlje (eng. shebang) je prvi red skripte, koji počinje znakovima #!. On određuje koji program treba da izvrši skriptu kada se pokrene kao izvršna datoteka.
#!/bin/bash
#!/usr/bin/python
#!/usr/bin/env bashSkript se nalazi u fajlu 01_shebang.
6.2 Argumenti i zamene parametara
Pozicioni parametri predstavljaju argumente prosleđene skripti: $0 je ime skripte, $1, $2 i ostali su pojedinačni argumenti, a $# je njihov broj. "$@" prosleđuje svaki argument kao zasebnu celinu, uključujući argumente sa razmacima.
$ ./02_args first "second arg"Script: ./02_args
Argument count: 2
Argument: <first>
Argument: <second arg>
Skript se nalazi u fajlu 02_args.
Napomena: Proslediti argument koji sadrži razmak
6.2.1 Zamene
Primer pokazuje podrazumevane vrednosti, broj znakova i izdvajanje dela teksta:
$ ./03_substitutionsHello, Guest
File: report.txt
Uppercase name: GUEST
First three characters: Gue
Character count: 5
Skript se nalazi u fajlu 03_substitutions.
Oblici zamene parametara koji se često koriste:
${var:-default} # Koristi default ako je var nedefinisan ili prazan.
${var-default} # Koristi default samo ako var nije definisan.
${var:?message} # Ispiši poruku i završi ako je var nedefinisan ili prazan.
${var:=default} # Dodeli default ako je var nedefinisan ili prazan.6.2.2 Zamena komande i aritmetička ekspanzija
Zamena komande čuva standardni izlaz komande u promenljivoj. Koristite oblik $(...), koji je pregledniji od starih povratnih navodnika (``).
$ ./04_command_substToday: 2024-01-15
Files in current directory: 5
Skript se nalazi u fajlu 04_command_subst.
Aritmetička ekspanzija služi za celobrojne izraze:
$ ./05_arithmeticcount=11, next=8
Skript se nalazi u fajlu 05_arithmetic.
6.3 Provera argumenata i parsiranje opcija
6.3.1 Duge opcije ručnim upravljanjem
getopts obrađuje kratke opcije. Za jednostavan skup dugih opcija može se koristiti petlja:
$ ./06_parse_long --verbose --output result.txt input.txtInput: input.txt
Output: result.txt
Oznaka -- završava obradu opcija. Korisna je kada ime datoteke počinje crticom:
$ ./06_parse_long -- -odd-filename.txtSkript se nalazi u fajlu 06_parse_long.
6.3.2 Kratke i duge opcije zajedno
getopts obrađuje kratke opcije. Za kombinovano parsiranje kratkih i dugih opcija koristi se getopt:
$ ./07_parse_options -v -o result.txt input.txt
$ ./07_parse_options --verbose --output result.txt input.txt
$ ./07_parse_options -o result.txt --verbose input.txtSkript se nalazi u fajlu 07_parse_options.
6.4 Uslovi i status komande
Status komande je 0 kada je uspešna, a različit od nule kada nije. Na primer, grep vraća status 1 kada ne pronađe podudaranje.
$ ./08_command_statusErrors found
Skript se nalazi u fajlu 08_command_status.
Uslovi za datoteke mogu da se izraze pomoću [ ] operatora:
$ ./09_file_conditions /tmpDirectory: /tmp
Skript se nalazi u fajlu 09_file_conditions.
Operatori && i || pokreću sledeću komandu u zavisnosti od statusa prethodne:
mkdir -p output && printf 'Output directory is ready\n'6.5 Funkcije, nizovi i petlje
6.5.1 Funkcije i statusi
Funkcija vraća status poslednje izvršene komande, osim ako status nije izričito postavljen pomoću return. local ograničava promenljivu na funkciju.
$ ./10_functions /etc/passwdFile is readable
Skript se nalazi u fajlu 10_functions.
6.5.2 Nizovi
Nizovi čuvaju listu vrednosti bez gubitka razmaka ili posebnog značenja znakova kao što je *.
$ ./11_arraysThere are 3 entries
Entry: <first file.txt>
Entry: <second.txt>
Entry: <*.log>
All entries: <first file.txt> <second.txt> <*.log> <new file.csv>
Skript se nalazi u fajlu 11_arrays.
Koristite "${array[@]}" kada treba da se svaki element prosledi kao poseban argument.
6.5.3 Bezbedna obrada imena datoteka
Imena datoteka mogu da sadrže razmake, tabulate, pa čak i nove redove. Kombinacija find -print0 i read -d '' bezbedno obrađuje takva imena.
$ ./12_safe_filenamesSkript se nalazi u fajlu 12_safe_filenames.
6.6 Redirekcija ulaza i izlaza
Proces obično ima tri standardna toka: standardni ulaz (0), standardni izlaz (1) i standardni izlaz za greške (2).
$ ./13_redirectionsSkript se nalazi u fajlu 13_redirections.
Redosled redirekcija je važan. > run.log 2>&1 prvo preusmerava standardni izlaz u datoteku, a zatim preusmerava izlaz za greške tamo gde je standardni izlaz trenutno usmeren.
6.6.1 “Here”-dokument i “here”-niska
“Here”-dokument prosleđuje više redova kao standardni ulaz:
$ ./14_here_docHello, class.
This text is supplied on standard input.
Skript se nalazi u fajlu 14_here_doc.
Ako je oznaka razgraničenja pod navodnicima, promenljive i komande se ne proširuju:
$ ./15_here_doc_literalThis is literal text: $HOME is not expanded.
Skript se nalazi u fajlu 15_here_doc_literal.
“Here”-niska prosleđuje jedan tekstualni niz, uz završni novi red:
$ ./16_here_stringhaystack with a needle
Skript se nalazi u fajlu 16_here_string.
6.7 Cevovodi i tee
Cevovod (|) prosleđuje standardni izlaz jedne komande na standardni ulaz sledeće.
$ ./17_pipesSkript se nalazi u fajlu 17_pipes.
tee istovremeno prikazuje ulaz na standardnom izlazu i upisuje ga u datoteku. Sa opcijom -a dodaje sadržaj na kraj datoteke.
$ ./18_tee_logSkript se nalazi u fajlu 18_tee_log.
Primer beleženja izlaza komande:
$ ./19_pipefailSkript se nalazi u fajlu 19_pipefail.
Bez pipefail, status cevovoda je obično status poslednje komande. Sa pipefail, cevovod prijavljuje neuspeh ako ne uspe neka od komandi u njemu.
6.8 Privremene datoteke i čišćenje
mktemp kreira jedinstveno ime privremene datoteke. trap pokreće funkciju za čišćenje pri izlasku iz skripte.
$ ./20_temp_filesSkript se nalazi u fajlu 20_temp_files.
Primer sa privremenim direktorijumom:
$ ./21_temp_dirSkript se nalazi u fajlu 21_temp_dir.
Oznaka -- ukazuje da se argumenti opcija završavaju. To sprečava da se putanja koja počinje crticom protumači kao opcija komande.
6.9 Signali
trap povezuje komandu ili funkciju sa signalom ili završetkom skripte. Ctrl-C obično šalje signal INT; signal TERM traži da se proces zaustavi.
$ ./22_signals &Pritisnite
Ctrl+Cda zaustavite skriptu.
Skript se nalazi u fajlu 22_signals.
Funkcija za čišćenje treba da bude jednostavna i bezbedna za pozivanje tokom završavanja skripte.
6.10 Pozadinski procesi
Znak & pokreće komandu u pozadini. PID poslednjeg pokrenutog pozadinskog procesa dostupan je u promenljivoj $!.
$ ./23_backgroundStarted jobs 5678 and 5679
First job exit status: 0
Second job exit status: 0
Skript se nalazi u fajlu 23_background.
wait PID čeka završetak procesa i vraća njegov status. Sačuvajte $! odmah nakon pokretanja procesa, jer se vrednost menja nakon sledeće pozadinske komande.
6.10.1 Pokretanje više zadataka i provera statusa
$ ./24_multi_taskalpha: starting
beta: starting
beta: finished
alpha: finished
All tasks succeeded
Skript se nalazi u fajlu 24_multi_task.
Izlaz pozadinskih procesa može da se prepliće. Da biste sačuvali izlaz svakog zadatka zasebno, redirektujte ga u posebnu datoteku:
$ ./25_separate_outputSkript se nalazi u fajlu 25_separate_output.
6.10.2 Ograničenje broja paralelnih zadataka
wait -n čeka da se završi bilo koji od navedenih pozadinskih zadatka. Primer ograničava broj istovremenih zadataka na dva.
$ ./26_parallel_limitProcessing alpha
Processing beta
alpha: finished
Processing gamma
Processing delta
beta: finished
All tasks finished
Skript se nalazi u fajlu 26_parallel_limit.
wait -n je dostupan u novijim verzijama Bash-a.
6.10.3 Zaustavljanje pozadinskih procesa tokom čišćenja
Skripta koja pokreće pozadinske procese treba po potrebi da ih zaustavi i sačeka pri izlasku.
$ ./27_cleanup_bg &Pritisnite
Ctrl+Cda zaustavite skriptu.
Skript se nalazi u fajlu 27_cleanup_bg.
Ovaj primer šalje signal za prekid i čeka procese. Složenije skripte možda moraju da vode računa i o procesima koje su pokrenuli njihovi pozadinski zadaci.
6.11 Potprocesi i zamena procesa
Komande u zagradama pokreću se u potprocesu (eng. subshell). Promena direktorijuma ili vrednosti promenljive unutar potprocesa ne menja roditeljski proces.
$ ./28_subshellBefore: /home/user
Inside subshell: /tmp
After: /home/user
Skript se nalazi u fajlu 28_subshell.
Zamena procesa omogućava da se izlaz komande koristi kao datoteka slična ulazu. U ovom primeru, diff dobija dva zasebna ulaza:
$ ./29_proc_substSkript se nalazi u fajlu 29_proc_subst.
6.12 Primer: brojanje podudaranja
Ova skripta kombinuje parsiranje opcija, proveru ulazne datoteke, privremenu datoteku, čišćenje, obradu izlaznog statusa komande i opcioni izlaz u datoteku.
$ ./30_count_matches -v ERROR application.log
$ ./30_count_matches -o matches.txt ERROR application.logSkript se nalazi u fajlu 30_count_matches.
6.13 Zadaci za vežbu
- Dodajte opciju
-nkoja ograničava broj prikazanih rezultata. - Izmenite primer sa
teetako da dodaje sadržaj u dnevnik umesto da ga zamenjuje. - Dodajte proveru da se ulazna datoteka razlikuje od izlazne.
- Izmenite primer sa pozadinskim procesima tako da svaki zadatak upisuje rezultat u zasebnu datoteku.
- Testirajte skripte sa praznim argumentima, nepostojećim datotekama i imenima koja sadrže razmake.