Da li ste ikada željeli da uzmete podatke u jednom formatu i pretvorite ih u drugi format? Naravno da jeste! To je, generalno gledano, tema ove lekcije. Tačnije, upravljanje podacima, bilo u tekstualnom ili binarnom formatu, sve dok ne dođete do onoga što želite.
Već smo vidjeli neke osnove upravljanja podacima u prethodnim lekcijama. Manje-više kada god koristite
1
|
1
journalctl | grep -i intel
Krenimo od početka. Da bi upravljali podacima, potrebne su nam dvije stvari: podaci kojima upravljamo, i nešto što ćemo sa njima da uradimo. Logs su obično dobar primjer, jer često želite da ispitate stvari koje su vezane za njih, a kompletno čitanje nije izvodljivo. Hajde da shvatimo ko pokušava da se uloguje u moj server posmatranjem mog server log-a:
1
ssh myserver journalctl
To je previše stvari. Ograničimo to na ssh stvari:
1
ssh myserver journalctl | grep sshd
Primjetite da koristimo pajp za striming udaljene datoteke kroz
1
grep
1
ssh
1
ssh myserver 'journalctl | grep sshd | grep "Disconnected from"' | less
Zašto dodatno citiranje? Pa, naši logs mogu biti veoma veliki, suvišno je da sve to strimujemo na naš računar i onda da ih filtriramo. Umjesto toga, možemo odraditi filtriranje na udaljenom serveru, a zatim proslijediti podatke lokalno.
1
less
1 2
$ ssh myserver 'journalctl | grep sshd | grep "Disconnected from"' > ssh.log $ less ssh.log
Ovdje postoji još mnogo buke. Postoji još mnogo načina da se toga riješite, ali hajde da pogledamo jedan od najmoćnijih alata koji su vam na raspolaganju:
1
sed
1
sed
1
ed
1
s
1 2 3 4
ssh myserver journalctl | grep sshd | grep "Disconnected from" | sed 's/.*Disconnected from //'
Ovo što smo upravo napisali je jednostavni regularni izraz; moćan konstrukt koji vam pruža mogućnost da uparujete tekst sa obrascima. Komanda
1
s
1
s/REGEX/SUBSTITUTION/
1
REGEX
1
SUBSTITUTION
(Možda možete prepoznati ovu sintaksu iz "Pronađi i zamijeni" sekcije iz naših Vim Zabilješke lekcije! Zaista, Vim koristi sintaksu da pronađe i zamijeni koja je slična sa
1
sed
Regularni izrazi
Regularni izrazi su uobičajeni i dovoljno korisni da je pametno uložiti vrijeme da bi shvatili kako funkcionišu. Počnimo posmatrajući primjer odozgo:
1
/.*Disconnected from /
1
/
- znači "bilo koji pojedinačni karakter" osim novog reda
1
.
- nula ili više prethodnih se poklapa
1
*
- jedan ili više prethodnih se poklapa
1
+
- bilo koji od karaktera
1
[abc]
,1
a
,1
b
1
c
- nešto što se poklapa ili sa RX1 ili sa RX2
1
(RX1|RX2)
- početak reda
1
^
- kraj reda
1
$
1
sed
1
\
1
-E
Gledajući ponovo na
1
/.*Disconnected from /
1
Jan 17 03:13:00 thesquareplanet.com sshd[2631]: Disconnected from invalid user Disconnected from 46.97.239.16 port 55920 [preauth]
Sa čim bi završili? Pa,
1
*
1
+
1
46.97.239.16 port 55920 [preauth]
To može biti ono što nismo željeli. U nekim implementacijama regularnih izraza, možete samo dodati
1
*
1
+
1
?
1
sed
1
perl -pe 's/.*?Disconnected from //'
Držaćemo se
1
sed
1
sed
1
sed
U redu, takođe imamo sufix kojeg bi da se riješimo. Kako bi mogli to da uradimo? Malo je nezgodno izvršiti poklapanje teksta koji prati korisničko ime, posebno ukoliko ime ima razmake i slično. Ono što moramo da uradimo jeste da se podudari čitava linija:
1
| sed -E 's/.*Disconnected from (invalid |authenticating )?user .* [^ ]+ port [0-9]+( \[preauth\])?$//'
Hajde da pogledamo šta se dešava sa regex debugger. U redu, početak je isti kao i ranije. Onda, vrišimo podudaranje sa bilo kojom varijantom "korisnika" (postoje dva prefixa u logs). Onda vršimo podudaranje bilo kojeg stringa gdje je korisničko ime. Onda vršimo podudaranje bilo koje pojedinačne riječi
1
([^ ]+
1
[preauth]
Primjećujete da koristeći ovu vrstu tehnike, korisničko ime "Disconnected from" nas više ne zbunjuje. Da li možete da vidite zašto?
Postoji jedan problem sa ovim, a to je da čitav log postaje prazan. Mi želimo da zadržimo korisničko ime nakon svega. Za ovo, možemo koristiti "hvatanje grupa". Bilo koji tekst koji se poklapa sa regex-om okruženim zagradom se čuva u grupi koja je označena brojem. Ove su dostupne u zamjeni (a u nekim engin-ima, čak i sam obrazac) kao
1
\1
1
\2
1
\3
1
| sed -E 's/.*Disconnected from (invalid |authenticating )?user (.*) [^ ]+ port [0-9]+( \[preauth\])?$/\2/'
Kao što vjerovatno možete da zamislite, možete dobiti veoma složeni regularni izraz. Na primer, evo jednog članka o tome kako bi mogli da izvršite podudaranje sa e-mail adresom. Nije lako. I postoji dosta rasprave. I ljudi su pisali testove. I matrice testova. Možete čak i napisati regex da bi provjerili da li je zadati broj prost broj.
Regularni izrazi su notorno teški za pročitati, ali ih je jako pogodno znati.
Nazad na upravljanje podacima
U redu, sada imamo
1 2 3 4
ssh myserver journalctl | grep sshd | grep "Disconnected from" | sed -E 's/.*Disconnected from (invalid |authenticating )?user (.*) [^ ]+ port [0-9]+( \[preauth\])?$/\2/'
1
sed
1
i
1
p
1
men sed
Da se vratimo na stvar. Ono što imamo sada je lista svih korisničkih imena koji su pokušali da se uloguju. Ali ovo i nije baš korisno. Hajde da pogledamo neke česte primjere:
1 2 3 4 5
ssh myserver journalctl | grep sshd | grep "Disconnected from" | sed -E 's/.*Disconnected from (invalid |authenticating )?user (.*) [^ ]+ port [0-9]+( \[preauth\])?$/\2/' | sort | uniq -c
1
sort
1
uniq -c
1 2 3 4 5 6
ssh myserver journalctl | grep sshd | grep "Disconnected from" | sed -E 's/.*Disconnected from (invalid |authenticating )?user (.*) [^ ]+ port [0-9]+( \[preauth\])?$/\2/' | sort | uniq -c | sort -nk1,1 | tail -n10
1
sort -n
1
-k1,1
1
,n
1
n
Ukoliko bi željeli one koje su najređe, možemo koristiti
1
head
1
tail
1
sort -r
U redu, to je bilo prilično dobro, ali šta ukoliko bi željeli da izvedemo samo korisnička imena sa listom koja je odvojena zarezom umjesto novim redom, možda za config datoteku?
1 2 3 4 5 6 7
ssh myserver journalctl | grep sshd | grep "Disconnected from" | sed -E 's/.*Disconnected from (invalid |authenticating )?user (.*) [^ ]+ port [0-9]+( \[preauth\])?$/\2/' | sort | uniq -c | sort -nk1,1 | tail -n10 | awk '{print $2}' | paste -sd,
Počnimo sa
1
paste
1
-s
1
-d
1
,
1
awk
awk - drugi editor
1
awk
1
awk
Prvo, šta
1
{print $2}
1
awk
1
$0
1
$1
1
$n
1
n
1
awk
1
-F
Hajde da vidimo da li možemo nešto da uradimo još bolje. Hajde da izračunamo broj korisničkih imena za jednu upotrebu koji počinju sa
1
c
1
e
1
| awk '$1 == 1 && $2 ~ /^c[^ ]*e$/ { print $2 }' | wc -l
Mnogo toga je neraspakovano ovdje. Prvo, primjećujete da sada imam obrazac (stvari koje idu prije
1
{...}
1
uniq -c
1
wc -l
Ipak,
1
awk
1 2 3
BEGIN { rows = 0 } $1 == 1 && $2 ~ /^c[^ ]*e$/ { rows += $1 } END { print rows }
1
BEGIN
1
END
1
grep
1
sed
1
awk
Analiziranje podataka
Možete koristiti matematiku direktno u vašem shell-u koristeći
1
bc
1
+
1
| paste -sd+ | bc -l
Ili napraviti složenije izraze:
1
echo "2*($(data | paste -sd+))" | bc -l
Možete dobiti statistiku na više načina. st je veoma uredan, ali ako već imate R:
1 2 3 4 5 6
ssh myserver journalctl | grep sshd | grep "Disconnected from" | sed -E 's/.*Disconnected from (invalid |authenticating )?user (.*) [^ ]+ port [0-9]+( \[preauth\])?$/\2/' | sort | uniq -c | awk '{print $1}' | R --slave -e 'x <- scan(file="stdin", quiet=TRUE); summary(x)'
R je još jedan (čudan) programski jezik koji je odličan za analizu podataka i plotting. Nećemo puno zalaziti u detalje, ali je dovoljno reći da
1
rezime
Ukoliko samo želite jednostavan plotting,
1
gnuplot
1 2 3 4 5 6 7
ssh myserver journalctl | grep sshd | grep "Disconnected from" | sed -E 's/.*Disconnected from (invalid |authenticating )?user (.*) [^ ]+ port [0-9]+( \[preauth\])?$/\2/' | sort | uniq -c | sort -nk1,1 | tail -n10 | gnuplot -p -e 'set boxwidth 0.5; plot "-" using 1:xtic(2) with boxes'
Upravljanje podacima da bi se napravili argumenti
Ponekad želite da upravljate podacima da bi pronašli stvari koje ćete da instalirate ili uklonite bazirajući se na nekoj dužoj listi. Upravljanje podacima o kojem smo pričali do sada +
1
xargs
Na primer, kao što ste vidjeli u lekciji, mogu koristiti sledeću komandu da deinstaliram stari build Rust-a iz mog sistema izvodeći stare nazive build-a koristeći alate za upravljanje podacima i prosleđujući ih kroz
1
xargs
1
rustup toolchain list | grep nightly | grep -vE "nightly-x86" | sed 's/-x86.*//' | xargs rustup toolchain uninstall
Upravljanje binarnim podacima
Do sada, najviše smo pričali o upravljanju tekstualnim podacima, ali pajpovi su takođe korisni i za binarne podatke. Na primer, možemo koristiti ffmpeg da snimimo sliku iz naše kamere, konvertujemo je u grayscale, kompresujemo je, pošaljemo je na udaljenu mašinu preko SSH-a, tamo je dekompresujemo, napravimo kopiju, i onda je prikažemo.
1 2 3 4
ffmpeg -loglevel panic -i /dev/video0 -frames 1 -f image2 - | convert - -colorspace gray - | gzip | ssh mymachine 'gzip -d | tee copy.jpg | env DISPLAY=:0 feh -'
Vježbe
- Pogledajte ovaj kratki interaktivni regex tutorijal
- Pronađite broj riječi (u ) koje sadrže bar tri
1
/usr/share/dict/words
i nemaju1
a
na kraju. Koje su tri kombinacije najčešća poslednja dva slova te riječi?1
s
i1
sed
komanda, ili1
y
program vam može pomoći sa neosjetljivošću velikih slova. Koliko je tih kombinacija sa dva slova tu? I za izazov: Koje kombinacije se ne pojavljuju?1
tr
- Da bi se uradila zamjena u mjestu veoma je izazovno uraditi nešto kao . Ipak je ovo loša ideja, zašto? Da li je ovo posebno vezano za
1
sed s/REGEX/SUBSTITUTION/ input.txt > input.txt
? Koristite1
sed
da bi saznali kako ovo da odradite.1
man sed
- Pronađite vaš prosjek, medijanu, i najduže vrijeme podizanja sistema za poslednjih deset puta. Koristite na Linuxu i
1
journalctl
na macOS, i potražite vremenske oznake log-a blizu početka i kraja svakog pokretanja. Na Linuxu, oni mogu izgledati ovako:1
log show
1
Logs begin at ...
i
1
systemd[577]: Startup finished in ...
Na macOS, potražite:
1
=== system boot:
i
1
Previous shutdown cause: 5
- Potražite boot poruke koje nisu podijeljenje između vaša 3 poslednja podizanja sistema (pogledajte
1
journalctl
flag). Podijelite ovaj zadatak na više koraka. Prvo nađite način da dobijete log iz poslednja 3 podizanja sistema. Može postojati odgovarajući flag na alatu koji koristite da bi izvukli log podizanja sistema, ili možete koristiti1
-b
da uklonite sve linije prije one koja se poklapa sa1
sed '0,/STRING/d'
. Dalje, uklonite sve dijelove reda koji uvijek varira (kao vremenski žig). Zatim, de-duplicirajte linije inputa i zadržite broj svake od njih (1
STRING
je vaš prijatelj). I konačno, elminišite bilo koji red čiji je brojač 3 (budući da je bio podijeljen u svim podizanjima sistema).1
uniq
- Pronađite skup podataka online kao što je ovaj, ili ovaj ili možda ovaj. Dohvatite ih koristeći i izvadite samo dvije kolone numeričkih podataka. Ukoliko povlačite HTML podatke, pup može biti od koristi. Za JSON podatke, pokušajte sa jq. Pronađite minimum i maksimum jedne kolone u jednoj komandi, i zbir razlika između dvije kolone u drugoj.
1
curl