Piping i redirekcija
Ovo je ono što od Linuxa pravi Linux. Nijedna komanda ne radi sve — ali se male komande spajaju u lanac koji rešava problem za koji na drugom sistemu treba program.
Cela ideja stoji na jednom pravilu: svaki program ima jedan ulaz i dva izlaza, i sva tri se mogu preusmeriti gde god hoćeš. Kad to razumeš, ostatak je sintaksa.
Tri toka
Kad se program pokrene, sistem mu automatski otvori tri kanala:
| 0 | stdin | standardni ulaz — podrazumevano tastatura |
| 1 | stdout | standardni izlaz — podrazumevano ekran |
| 2 | stderr | standardna greška — takođe ekran |
Brojevi nisu ukras; koristićeš ih doslovno u komandama. Zato
2> znači „preusmeri kanal broj 2", odnosno greške.
Zašto su izlaz i greška razdvojeni, iako oba završe na ekranu? Da bi rezultat mogao da se prosledi dalje, a poruke o problemu da i dalje stignu do tebe. To je razlika koju ćeš najlakše videti na primeru:
$ ls postoji.txt nepostoji.txt ls: cannot access 'nepostoji.txt': No such file or directory postoji.txt $ ls postoji.txt nepostoji.txt > spisak.txt ls: cannot access 'nepostoji.txt': No such file or directory $ cat spisak.txt postoji.txt
U fajl je otišao samo izlaz. Greška je ostala na ekranu jer je putovala drugim kanalom.
Izlaz u fajl: > i >>
$ echo "prvi red" > dnevnik.txt $ echo "drugi red" > dnevnik.txt $ cat dnevnik.txt drugi red
> briše postojeći sadržaj. Za dodavanje na kraj
koristi se >>:
$ echo "prvi red" > dnevnik.txt $ echo "drugi red" >> dnevnik.txt $ cat dnevnik.txt prvi red drugi red
Dve stvari koje ovde treba primetiti, jer se na njima gubi vreme:
Prvo: preusmeravanje ne radi komanda, nego shell — isto kao sa džokerima iz lekcije Džokeri i navodnici. Shell otvori fajl pre nego što pokrene komandu.
Drugo, i mnogo bolnije: pošto shell fajl otvori i isprazni pre pokretanja komande, ovo uništava podatke:
$ sort spisak.txt > spisak.txt $ cat spisak.txt $
Fajl je prazan. Shell ga je ispraznio, pa je sort dobio prazan
ulaz i u njega upisao ništa. Nema oporavka. Ispravno je preko privremenog
fajla:
$ sort spisak.txt > /tmp/sortirano.txt && mv /tmp/sortirano.txt spisak.txt
Neki alati imaju ugrađen izlaz iz ovog problema:
sort -o spisak.txt spisak.txt ili
sed sa opcijom -i.
Zaštita od slučajnog prepisivanja
$ set -o noclobber $ echo test > dnevnik.txt bash: dnevnik.txt: cannot overwrite existing file
Kad ti stvarno treba prepisivanje, koristi >|. Red
set -o noclobber može trajno u ~/.bashrc, ali imaj u
vidu da ćeš na tuđem serveru raditi bez te mreže.
Greške: 2>
$ find /etc -name "*.conf" 2> /dev/null /etc/ssh/ssh_config /etc/sysctl.conf ...
/dev/null je sistemska „kanta" — sve što u nju upišeš nestaje.
Ovo je najčešća upotreba 2>: kad
find pretražuje sistem kao običan korisnik,
polovina ispisa je Permission denied i zatrpava rezultate.
Kad hoćeš suprotno — samo greške, bez rezultata:
$ find /etc -name "*.conf" > /dev/null
Greške u fajl, za kasniju analizu:
$ ./instalacija.sh 2> greske.log
Oba toka na isto mesto
Ovaj oblik je najkraći i radi u bash-u:
$ ./instalacija.sh &> sve.log $ ./instalacija.sh &>> sve.log
Klasičan, prenosiv oblik koji ćeš sretati u tuđim skriptama:
$ ./instalacija.sh > sve.log 2>&1
2>&1 se čita kao „kanal 2 neka ide tamo gde trenutno
ide kanal 1". Reč trenutno je ključna, jer redosled je bitan:
$ ./skripta.sh 2>&1 > sve.log
Ovo ne radi ono što izgleda. Kad se izvrši 2>&1,
kanal 1 još uvek ide na ekran — pa greške odlaze na ekran. Tek posle toga se
izlaz preusmerava u fajl. Rezultat: u fajlu je samo izlaz, greške su na
ekranu.
Pravilo koje vredi zapamtiti: 2>&1 uvek ide
posle preusmeravanja izlaza.
Ulaz iz fajla: <
$ wc -l < spisak.txt 42
Razlika u odnosu na wc -l spisak.txt je suptilna ali korisna:
kad se koristi <, komanda ne zna ime fajla, pa ga ne ispisuje
uz rezultat. Zgodno kad ti treba samo broj, na primer u skripti.
Heredoc — više redova teksta u komandu
$ cat > podesavanja.conf << EOF Port 2222 PermitRootLogin no EOF $ cat podesavanja.conf Port 2222 PermitRootLogin no
Sve do reda koji sadrži samo EOF ide na ulaz komande. Oznaka
ne mora biti baš EOF, to je samo običaj. Ako ne želiš da shell
dira promenljive unutar bloka, stavi oznaku pod navodnike:
$ cat << 'EOF' Cena je $5, a putanja je $HOME EOF Cena je $5, a putanja je $HOME
Herestring — jedan red
$ grep -c "linux" <<< "linux je linux i ostaje linux" 1
Cev: |
Do sada smo tokove vezivali za fajlove. Cev radi nešto drugo — spaja izlaz jedne komande sa ulazom druge, bez fajla između:
$ ps aux | grep nginx
Nema privremenog fajla, nema čekanja da prva komanda završi. Podaci teku kroz memoriju dok obe komande rade uporedo. Zato ovo radi trenutno i na ogromnom fajlu:
$ cat /var/log/syslog | head -3
head uzme tri reda i završi, a cat dobije signal
da prestane. Nikad se ne pročita ceo fajl.
Lanac može biti proizvoljno dugačak. Klasičan primer — deset IP adresa sa najviše neuspelih prijava:
$ grep "Failed password" /var/log/auth.log | awk '{print $11}' | sort | uniq -c | sort -rn | head
847 203.0.113.45
312 198.51.100.7
28 192.0.2.19
Pet komandi, svaka radi jednu stvar:
grep izdvaja redove,
awk uzima jedanaestu kolonu,
sort grupiše iste,
uniq -c broji, drugi sort ređa opadajuće po broju.
Savet iz prakse: ovakav lanac se ne piše odjednom. Pokreni prvu komandu, pogledaj ispis, dodaj drugu, pogledaj ponovo. Kad nešto ne radi u lancu od pet komandi, greška je skoro uvek u drugoj ili trećoj — a mnogo je brže naći je odmah nego kasnije.
Šta se dešava sa greškama u cevi
Kroz cev prolazi samo stdout. Greške i dalje idu na ekran:
$ ls postoji.txt nepostoji.txt | wc -l ls: cannot access 'nepostoji.txt': No such file or directory 1
Ako hoćeš da i greške prođu kroz cev, spoji ih pre nje:
$ ls postoji.txt nepostoji.txt 2>&1 | wc -l 2
U bash-u postoji i skraćenica |&, koja znači isto.
Izlazni status lanca
Ovo redovno ujeda ljude koji pišu skripte:
$ nepostojeca-komanda | tee izlaz.log bash: nepostojeca-komanda: command not found $ echo $? 0
Nula znači uspeh — iako komanda nije ni postojala. Lanac vraća status
poslednje komande, a tee je uredno uspeo. Rešenje na vrhu
skripte:
set -o pipefail
$ set -o pipefail $ nepostojeca-komanda | tee izlaz.log bash: nepostojeca-komanda: command not found $ echo $? 127
Statuse svih članova lanca pojedinačno drži niz
PIPESTATUS:
$ echo "${PIPESTATUS[@]}"
127 0
tee — i na ekran i u fajl
Cev ima jedan izlaz. Kad ti treba i da vidiš ispis i da ga sačuvaš, tu je tee:
$ ./instalacija.sh | tee instalacija.log Provera zavisnosti... Instalacija u toku... Gotovo. $ ls -l instalacija.log -rw-r--r-- 1 alen alen 58 Aug 22 12:04 instalacija.log
Opcija -a dodaje umesto da prepisuje. A tee može
i usred lanca, kao mesto na kome „prisluškuješ" međurezultat:
$ grep ERROR app.log | tee greske.txt | wc -l 17
Najvažnija upotreba je pisanje u sistemski fajl. Ovo ne radi:
$ sudo echo "127.0.0.1 test.local" >> /etc/hosts bash: /etc/hosts: Permission denied
Razlog: preusmeravanje obavlja tvoj shell, pre nego što
sudo uopšte krene. Sudo se odnosi na
echo, a echo nije problem. Rešenje je da sa root
pravima radi ono što piše u fajl:
$ echo "127.0.0.1 test.local" | sudo tee -a /etc/hosts > /dev/null
Na kraju je > /dev/null samo zato što bi tee
inače ponovio red i na ekranu.
Komande koje ne čitaju sa ulaza
$ ls *.txt | rm rm: missing operand
rm ne čita stdin — očekuje imena kao
argumente. Cev ne pomaže. Za takve komande postoji xargs, koji
ono što stigne sa ulaza pretvara u argumente:
$ find . -name "*.tmp" | xargs rm
Bezbednija varijanta, koja preživljava imena sa razmacima:
$ find . -name "*.tmp" -print0 | xargs -0 rm
Isto pravilo važi i za echo: echo ne čita ulaz,
pa ls | echo ispiše prazan red. Ako ti treba da vidiš šta prolazi
kroz cev, koristi cat ili tee.
Sitnice koje zbunjuju
Boje nestanu kad se ispis preusmeri
$ grep --color=auto ERROR app.log | less
Boje su nestale. Alati proveravaju da li pišu na terminal i sami gase bojenje kad ne pišu. Ako ih hoćeš i kroz cev:
$ grep --color=always ERROR app.log | less -R
-R govori pejdžeru da propusti kontrolne znakove — o čemu ima
više na stranici
less i more.
Ispis kroz cev kasni
$ tail -f app.log | grep ERROR
Ništa se ne pojavljuje minutima, iako greške stižu. Kad piše na terminal,
grep šalje red po red; kad piše u cev, čeka da se napuni bafer od
nekoliko kilobajta. Rešenje:
$ tail -f app.log | grep --line-buffered ERROR
Opštije, za alate bez te opcije, postoji stdbuf -oL. Više o
praćenju logova na stranici tail.
Promenljiva iz cevi „nestane"
$ cat spisak.txt | while read red; do broj=$((broj+1)); done $ echo $broj
Svaki član lanca radi u zasebnom potprocesu, pa promenljiva postavljena unutra ne postoji napolju. Zaobilaženje je preusmeravanje umesto cevi:
$ while read red; do broj=$((broj+1)); done < spisak.txt $ echo $broj 42
cat koji ništa ne radi
$ cat app.log | grep ERROR # nepotrebno $ grep ERROR app.log # isto, jedan proces manje
Prvi oblik nije greška i vidi se svuda, ali grep i skoro svi
alati za obradu teksta i sami umeju da otvore fajl.
Kratka referenca
> fajl— izlaz u fajl, prepisuje>> fajl— izlaz u fajl, dodaje na kraj2> fajl— greške u fajl2> /dev/null— odbaci greške&> fajl— i izlaz i greške u fajl> fajl 2>&1— isto, prenosivi oblik (redosled bitan)< fajl— ulaz iz fajla<< EOF— više redova teksta na ulaz<<< "tekst"— jedan red na ulazk1 | k2— izlaz prve na ulaz drugek1 2>&1 | k2— kroz cev prolaze i greške| tee fajl— i na ekran i u fajl| sudo tee -a fajl— dopisivanje u sistemski fajl| xargs komanda— ulaz pretvoren u argumenteset -o pipefail— lanac prijavi grešku bilo kog člana
Vežba
$ cd /var/log
Bez gledanja u rešenje, sastavi komande za sledeće:
- Prebroj koliko ukupno redova ima
syslog, bez ispisivanja imena fajla. - Pronađi deset najvećih fajlova u
/var/log, a poruke o nedostatku dozvola sakrij. - Izlistaj sve različite programe koji su pisali u
syslogdanas, sortirane po broju poruka. (Peta kolona je obično ime procesa — proveri sa cut iliawk.) - Isto kao pod 3, ali rezultat neka istovremeno ode i na ekran i u fajl
~/izvestaj.txt. - Dopiši red
# proverana kraj fajla/etc/hosts, kao običan korisnik.
Peti zadatak ima tačno jedno ispravno rešenje i cela lekcija vodi ka njemu.
Komande koje se najčešće nalaze u lancima obrađene su na stranicama grep, sort, cut, awk i tee.
Cela lekcija je deo vodiča Linux osnove.
Comments
Post a Comment