BASH — Parametry a I/O #
Osnova cvičení #
- procvičení psaní skriptů
- použití nových příkazů (
grep,find) - přesměrování vstupu a výstupu do souboru či dalšího příkazu
Soubory a streamy #
Přístup k souboru v rámci procesu je identifikován takzvaným “file descriptorem” (fd). File descriptor je celé číslo a platí, že
- standartní vstup (
stdin) máfdrovno 0, - standartní výstup (
stdout) máfdrovno 1, - standartní chybový výstup (
stderr) máfdrovno 2.
Z Unixové filosofie vychází, že všechno je soubor,
tedy i například ovladače k perifériím (jako sériová linka) - tyto soubory jsou pod adresářem /dev.
Pod /dev můžete najít i např. speciální soubory
jako
/dev/random: zapisuje pouze náhodná čísla/dev/null: nic nevypisuje a zápis do tohoto souboru nic nedělá: používá se pro zahození výstupu
Přesměrování výstupu a vstupu #
Přesměrování souborů #
Může se vám hodit uložit si výstup příkazu do souboru (pro pozdější zkoumání) pomocí operátoru >.
Defaultně je přesměrován pouze standartní výstup.
Pamatujte si, že když soubor obsahuje data, tak je > všechny přepíše. Pokud si přejete přidávat nová data
(tzv. append), tak místo > použijte >>.
cmd > soubor.txt # Přesměruje standartní výstup do soubor.txt
Spojení stderr s stdout se provede pomocí (povšimněte
si 1 a 2) 2>&1.
cmd > soubor.txt 2>&1 # Pozor na pořadí
Pro zahození standartního chybového výstupu použijte:
cmd 2>/dev/null
Pro zahození všeho
cmd > /dev/null 2>&1
Pro přesměrování stdout do stderr
cmd >&2
Pro přesměrování obsahu souboru na standartní vstup příkazu použijte:
cmd < vstup.txt
Přesměrování výstupu příkazu do jiného příkazu #
K tomuto se využívá takzvaná roura, angl. pipe.
V shellu se zapíše operátorem |. Pozor: přesměruje
se opět pouze standartní výstup příkazu.
Ukážeme na příkladu: příkaz grep "MAGIC" soubor.txt
vytiskne všechny řádky, které obsahují MAGIC, stdin.
Příkaz wc -l vytiskne počet řádek z stdin.
Následující příkaz vytiskne počet řádek obsahující MAGIC:
grep "MAGIC" soubor.txt | wc -l
Operátor roury můžete řetězit dále.
Here document #
Pokud si v shellu přejete přesměrovat více řádků najednou do příkazu, musíte použít konstrukci nazývanou “here document”.
Nápověda: příkaz
sort -nvytiskne sestupně seřazené řádky podle čísla na začátku řádky zadané ze standartního vstupu
Ukážeme na příkladu, kdy na vstupu z shellu máme seznam neseřazených čísel, na každé řádce jedno číslo. Chceme seřazený seznam vytisknout.
sort -n << EOF
6
2
100
EOF
Pokud chcete toto přesměrovat do souboru, použijte
sort -n << EOF > serazeno.txt
6
2
100
EOF
Text k přesměrování musí být ohraničen, v předchozích 2 příkladech
řetězcem EOF. Následující příklad bude také fungovat:
sort -n << FOOBAR
6
2
10
FOOBAR
Here string #
Další užitečnou konstrukcí sloužící pro přesměrování vstupu
je operátor <<<, aneb “here string”. Funguje jako here document,
ale nevyžaduje ohraničení a většinou se používá pro stringy s jednou řádkou.
Příklad:
grep "Ahoj" <<< "Ahoj svete a PSY"
Dá se také říci, že here document a here string nahrazují konstrukci
echo "RETEZEC" | cmd
Procvičení nových příkazů #
Referenci všech příkazů najdete zde. Ale probereme do detailu některé užitečné příkazy:
grep PATTERN: umí vyhledávat PATTERN v textu předaný ze standartního vstupu či souboru. V případě shody vytiskne příslušné řádky. Zajímavé přepínače:-n: vytiskne i číslo řádky-i: case insensitive-r: rekurzivně prohledává adresář pro všechny soubory obsahující PATTERN (nutno předat adresář)-o: vytiskne pouze místo shody, namísto celé řádky- vrací 0 pokud něco našel, jinak něco jiného
find path: rekurzivně najde soubory v daném podadresáři. Zajímavé přepínače:-name PATTERN: najde soubory splňující PATTERN (např.soubor.txtči"*.txt"). Pokud není tento přepínač specifikován, jsou prohledány všechny soubory-iname PATTERN: to stejné, akorát case insensitive-size VELIKOST: soubory zadané velikosti např.-size -1Mnebo-size +1M-mtime CAS: soubory změněné v daném čase, např-mtime -1změněné za posledních 24 hodin, nebo-mtime +60szměněné déle než 60 sekund.-exec: jestliže je v podadresáři nalezen soubor splňující PATTERN, je vykonán příkaz za-execna daném souboru. K referenci souboru je potřeba použít{}v příkazu za-exec.
tr: (translate characters) jednoduchá manipulace s textem (víceman tr)-d: ze vstupu odstraní string za -d-s: stejné znaky za sebou smrskne do jednoho
cut: rozdělí text v závislosti na oddělovači a vytiskne konkrétní sekci-d: specifikace oddělovače-f: specifikace pole, která se po oddělení vytiskno
⚠️ K zamyšlení: proč je argument
*.txtza-namev uvozovkách?
Příklady příkazů #
grep -rn "printf" src # Ve všech souborech v adresáři src najde výskyty
# slova printf, ty řádky vytiskne spolu s číslem řádky.
# grep vrací 0 v případě match - lze použít
if grep $match "soubor.txt"; then
echo "String $match byl v soubor.txt nalezen"
else
echo "String $match nebyl v soubor.txt nalezen
fi
# some_logger je nějaký příkaz, který kontinuálně vypisuje
# na standartní výstup log hlášky, grep pouze filtruje ty, které začínají
# na WARNING: a zapisuje je do souboru
some_logger | grep "WARNING:" > ./warning-logs.txt
# pro každý .txt soubor (v . a všech podadresářích) je spuštěn příkaz wc -l
find . -name "*.txt" -exec wc -l {} \; # reference matched souboru pomocí {},
# je nutno zadat \; pro další příkaz
Více příkazů v find může být složitější, viz. Odkazy a další materiály. Můžete použít např. toto řešení
# bash -c spustí nový shell s příkazem za -c
find . -name "*.txt" -exec bash -c "echo \"Nalezen soubor\"; wc -l {}"
# na vstupu máte .csv soubor (sloupce oddělené čárkou)
# úkolem je vypsat název knihy, který je v druhém sloupci
cut -f 2 -d ',' < autori.csv
# toto vypíše všechny sloupce od 2 až dále
cut -f 2- -d ',' < autori.csv
Vstup si můžete stáhnout zde.
Ze studentské tvořivosti #
Co udělá následující příkaz, když proměnná name='a.b.c.d.txt':
echo $name | tr '.' '\n' | tail -n 1
a co tento příkaz:
echo $name | tr '.' '\n' | head -n -1 | tr '\n' '.'
K procvičení #
- Napište všechny varianty toho, aby
greppřečetl soubor.txt. - Přepište podmínku s
grepvýše, aby při kontrolegrepnevypisoval na stdout. Též pro stderr. - Napište příkaz, který pro všechny soubory končící na .txt vytiskne jejich velikost
(použijte
du -b {} | cut -f 1). Zkuste vefindnapsat to, aby se mimo velikosti vytiskl i samotný soubor. - Napište skript (či složený příkaz), který z autori.csv vytáhne autory.
Následně vypíše jejich příjmení (vždy druhé slovo). Příjmení se opakují,
tedy pro finální výpis ještě použijte příkaz
sort -u(vytiskne lexikograficky seřazené řádky a pokud se nějaká opakuje, tak je vytisknuta pouze jednou) - Napište skript, který v
.spočte všechny soubory končící na .txt obsahující text PSY a toto číslo vytiskne. Jestliže nějaký příkaz selže, skript automaticky vrátí 1 a vypíše ERROR. Jestliže bylo nalezeno 0 souborů, skript vrátí 2 a vypíše NO FILES FOUND. Skript nevypisuje nic jiného! - Představte si, že pracujete na projektu a váš konkurent omylem zveřejnil
výpis jejich projektu, včetně uživatelských jmen a emailů.
Zjistěte všechny jejich usernames a emaily (pro strategické účely), které jsou ve formě
Pokud budou identické řádky, opět použijteJan Novak <novakjan@seznam.cz>.sort -u. Nápověda: zamyslete se, z jakých řádek můžete tuto informaci extrahovat. Jaký bude oddělovač a jaké fieldy použijete?