Třídění souborů podle přípon v Bashi, 2. část: Rozebrání skriptu


V dnešním dílu si skript rozebereme na jednotlivé části, ze kterých se skládá, a tyto části si popíšeme, abychom věděli, co dělají. Ze všeho nejdříve si náš jednořádkový skript / příkaz z prvního dílu přepíšeme do přehlednější víceřádkové podoby a rovnou z něj uděláme „plnohodnotný“ Bashový skript.

Pro koho to je?

Tento návod přímo navazuje na první díl, takže je to pro ty z vás, pro které je první díl a zároveň jste ho četli. 😉

Předem bych chtěla ujistit především ty, kteří o Bashi, terminálu či Linuxu vědí trochu víc, že vím, že Bash a terminál jsou trochu odlišné pojmy. Tento návod je určen spíše pro začátečníky či mírně pokročilé a nezachází do detailů, proto jsou v něm tyto pojmy občas zaměňovány. 🙂

Skript pro třídění souborů do složek podle přípony

#!/bin/bash

for soubor in *.*
do
    if [ -f "$soubor" ] && [ "$soubor" != *. ]
    then
        mkdir -p "${soubor##*.}" && mv "$soubor" $_ && echo "$soubor"
    fi
done

Hádám, že jste si všimli hned prvního řádku, že je to něco, co v předchozím skriptu nebylo, a přemýšlíte, k čemu to je? Tento speciální zápis říká systému, který program má použít pro zpracování tohoto skriptu, v našem případě je to Bash, který se standardně v Linuxu nachází v umístění /bin/bash. Tento zápis by měl být na prvním řádku každého Bashového skriptu.

Bash příkazy

Abychom mohli psát Bashové skripty, potřebujeme rozumět příkazům, které se v něm používají. Příkazy se zapisují do linuxového terminálu a potvrzují klávesou enter. Skript v podstatě není nic jiného, než posloupnost příkazů zapsaných v souboru, které se při spuštění toho souboru postupně vykonají a zpravidla to, co jde zapsat do terminálu, jde použít ve skriptu, a naopak. Příkladem může být náš skript pro třídění do složek podle přípony uvedený výše, jehož jednořádková podoba, uvedená v minulém dílu, je vlastně příkaz, který se dá jednoduše vložit do terminálu a pustit, je to prakticky ten samý skript.

Základní Bash příkazy

Nejprve bych vás ráda seznámila se základními příkazy, které umožňují základní orientaci v terminálu a pohyb mezi složkami. Pokud s Terminálem už umíte pracovat, můžete tuto část přeskočit.

Výpis obsahu složky: ls

Příkaz ls (LS psáno malými písmeny, je to zkratka z anglického slova „list“, což znamená „seznam“ nebo „výpis“) umí vypsat obsah složky, ve které se momentálně nacházíte. Když je otevřený terminál, vždy se nachází v nějaké složce (pracovním adresáři, po spuštění terminálu je to zpravidla uživatelská domovská složka), se kterým většina příkazů nějakým způsobem pracuje.

Za příkazy se dají často psát i přepínače, které ovlivňují jejich chování. Zpravidla se zapisují hned za příkaz, za který napíšete mezeru, pomlčku (přesněji spojovník či mínus, zkrátka znak „-„) a písmeno. Můžete zkusit jeden ze zápisů:

  • ls -l (malé písmeno L, způsobí výpis včetně podrobností),
  • ls -a (vypíše i skryté soubory a složky).

Lze také zapsat více přepínačů (písmen) za jednu pomlčku. Tato pravidla zápisu nemusí platit u každého příkazu, způsob zápisu přepínačů se u různých příkazů může lišit.

Za příkazy můžeme psát také parametry. Zatímco přepínače typicky ovliňují chování příkazu, parametry typicky určují příkazu jeho vstup (či vstupy). Například, zapíšeme-li za příkaz ls a jeho případné přepínače mezeru a název složky, vypíše příkaz obsah této složky nacházející se v aktuálním pracovním adresáři.

Např. zápis ls -la slozka způsobí podrobný výpis složky s názvem slozka uvnitř aktuálního pracovního adresáře, včetně skrytých souborů.

Ještě malá poznámka k pořadí: u příkazu ls se parametry zapisují až za přepínače, ale když pořadí prohodíte, nevadí mu to. Stejně tak nezáleží na pořadí jednotlivých přepínačů. Nemusí to tak ale platit u všech příkazů! Každý příkaz je vlastně samostatný program a Bash funguje jednoduše tak, že zavolá program, který je zapsaný na začátku (před první mezerou) a pošle mu celý příkaz ve formě jednotlivých argumentů (oddělených mezerou) v pořadí tak, jak jsou zapsány. Pak už záleží na konkrétním programu, jak si je zpracuje.

Přechod do jiné složky: cd

Příkaz cd (zkratka anglického „change directory“, tedy „změnit adresář“) umožňuje přejít do jiné složky (tedy změnit pracovní adresář). Pokud ho napíšete tak jak je, přesune vás do vaší uživatelské domovské složky. Jeho pravá síla spočívá v parametru, který se zapisuje za něj, oddělený mezerou. Ten říká, do které složky se chcete přesunout.

Takže pokud například napíšeme cd slozka, přesuneme se do podsložky s názvem „slozka“ uvnitř aktuálního pracovního adresáře (jinými slovy, pracovní adresář terminálu se nastaví do této složky).

Existuje také několik speciálních případů parametru:

  • cd . (přejde do aktuální složky – v podstatě nic nezmění, složka „.“ v Linuxu umožňuje odkazovat na aktuální složku, tento příkaz tedy funguje, ale vlastně nic neudělá)
  • cd .. (přejde do nadřazené / rodičovské složky, resp. o úroveň výše – složka „..“ v Linuxu odkazuje na nadřazenou složku, výjimkou je kořenový adresář, ve kterém ukazuje na kořenový adresář, tedy sama na sebe, stejně jako složka „.“)
  • cd - (tento parametr umožňuje přepnout do předchozího pracovního adresáře, ze kterého jsme přepnuli do aktuálního – pokud se použije vícekrát, umí přepínat mezi předposledním a posledním aktivním adresářem)
  • cd ~ (přejde do domovského adresáře aktuálního uživatele – „~“ je v Linuxu odkaz na váš domovský adresář, jehož celá cesta je typicky /home/user – pokud je uživatelské jméno „user“)

Pracovní adresář terminálu (tj. složka, ve které se aktuálně nacházíme) se zpravidla vypisuje na začátku řádku terminálu, případně se dá vypsat pomocí příkazu pwd (bez parametru).

Vytvoření složky: mkdir

Tento příkaz umí vytvořit novou složku – jednoduše jméno nové složky napíšeme za něj, jako jeho parametr – například zápis mkdir slozka vytvoří složku s názvem „slozka“.

Za pozornost rozhodně stojí jeho přepínač -p, který změní chování příkazu mkdir hned ve dvou směrech:

  • pokud složka už existuje, příkaz neskončí chybou, prostě jen nic neprovede,
  • v případě, že parametrem je cesta obsahující více složek (jinými slovy obsahuje lomítko, které není na začátku ani na konci, jelikož lomítko v Linuxu slouží pro oddělování podsložek), vytvoří postupně všechny složky a správně je zanoří do sebe tak, jak to má napsáno v parametru. Bez tohoto přepínače by se pokusil vytvořit pouze poslední složku (za posledním oddělujícím lomítkem).

Přesunutí: mv

Příkaz mv slouží pro přesouvání nebo přejmenování složek a souborů. Umí udělat oboje zároveň, záleží na jeho parametrech. Příkaz přijímá dva parametry, zdroj a cíl, resp. cílovou složku. Cíl je to, co určuje, co se se souborem stane:

  • Pokud je cílem název souboru/složky bez určení rodičovské složky přičemž zdroj je také bez určení rodičovské složky, resp. cíl i zdroj je ve stejné složce, provede se pouze přejmenování.
    Příklad: mv soubor1 soubor2
  • Pokud je cílem název existující složky, zdroj se do ní přesune. Pokud složka neexistuje, zdroj se na tento název přejmenuje. Pokud si chcete být jisti, že se soubor pouze přesune, napište na konec názvu cíle lomítko, Bash ho pak bude brát vždy jako složku.
    Příklad: mv soubor1 slozka2/
  • Pokud je cílem název souboru nebo neexistující složky uvnitř existující složky, soubor se tam přesune a zároveň přejmenuje na cílový název.
    Příklad: mv soubor1 slozka2/soubor2

Vypsání stromu složek: tree

Jednoduchý příkaz tree bez parametru dokáže hezky přehledně vypsat strom složek v aktuálním adresáři, tj. složky i všechny jejich podsložky (narozdíl od příkazu ls, který vypíše jen aktuální složku).

Tento příkaz není použitý ve skriptu, ale můžete si pomocí něj přehledně otestovat, jak skript funguje. Není ale součástí všech Linuxových distribucí, možná ho bude potřeba nejprve nainstalovat.

Vytvoření souboru: touch

Příkaz touch umí vytvořit prázdný soubor, přičemž název tohoto nového souboru odpovídá parametru příkazu touch.

Příklad: touch soubor

Tento příklad není použitý ve skriptu, může ale posloužit pro rychlé vytvoření testovacích souborů.

Pokročilé příkazy pro programování skriptů

Tyto příkazy se budou hodit především při psaní skriptů, ale lze je použít také přímo v terminálu.

Výpis obsahu proměnné: echo

Výpis obsahu proměnné se provádí pomocí příkazu echo, který jsme si ukázali už v prvním dílu. Příkaz ale umí vypsat nejen to, co má ve svém parametru (přesněji parametrech), ale především umí vypisovat obsah proměnných.

Jelikož se v Bashi proměnná při jejím použití zapisuje se znakem dolaru na začátku následovaným názvem proměnné, zápis příkazu pro vypsání obsahu proměnné je následující: echo $promenna.

Poznámka k psaní proměnných

Ráda bych se podělila o jeden tip z praxe (který využívám i ve skriptu, o kterém tento článek je): Pokud pracujete s proměnnými, zvlášť pokud do nich ukládáte názvy souborů či cesty k nim, uzavírejte je při používání do uvozovek. Bash totiž funguje tak, že při použití proměnné ($promenna) nejprve vezme tento zápis a nahradí ho skutečným obsahem proměnné, až pak zavolá samotný program a předá mu celý příkaz. Potíž může nastat ve chvíli, kdy hodnota proměnné obsahuje mezery. Pak se totiž ve skutečnosti zavolá například takovýto příkaz:

echo Ahoj světe!

V tomhle případě je to ještě v pořádku, jak jsme si už řekli, to, co je napsáno na příkazem, jsou jednotlivé parametry (pokud to nejsou přepínače) a zrovna příkaz echo se chová tak, že vezme všechny své parametry a vypíše je oddělené mezerami v tom samém pořadí, v jakém je dostal na vstupu. Koneckonců přesně tuhle podobu příkazu echo jsem ukazovala i jako příklad v minulém dílu.

Horší situace nastane ve chvíli, kdy máme například v proměnné s názvem promenna uložený název složky, kterou chceme vytvořit a zavoláme příkaz mkdir $promenna – pokud by byl název dvouslovný, např. „Ahoj světe!“ tak výsledný příkaz, který se ve skutečnosti zavolá, vypadá takto:

mkdir Ahoj světe!

No a v tomto případě příkaz mkdir vytvoří dvě složky – složku s názvem Ahoj a složku s názvem světe!. To asi není výsledek, který si autor skriptu přál, zvlášť ve chvíli, kdy skript v dalších příkazech počítá s ním, že exituje složka „Ahoj světe!“. Proto je určitě lepší použít zápis mkdir "$promenna", tedy uzavřít proměnnou do uvozovek, což Bash převede na mkdir "Ahoj světe!" a jelikož je ten název zapsaný v uvozovkách, počítá se to jako jeden parametr a tak to i Bash volanému programu předá.

Ještě je důležité zmínit, že to musí být klasické dvojité uvozovky, ne apostrofy – v těch zas Bash neinterpretuje proměnné, takže zápisem mkdir '$promenna' by se vytvořila složka s názvem $promenna. 😉

Pokud si chcete práci s proměnnými vyzkoušet, vlastní hodnotu do proměnné vložíte zápisem:

promenna=hodnota

Při přiřazování hodnoty se před název proměnné nepíše znak dolaru ($), pokud zadáváme hodnotu obsahující mezeru, je potřeba ji zapsat do uvozovek (ostatně, uvozovky můžeme použít vždy). Při zápisu je také důležité nedělat kolem znaku „=“ mezery.

promenna="Ahoj světe!"

Cyklus: for

Pokud umíte alespoň trochu základy programování, tenhle příkaz vám bude pravděpodobně známý. Jedná se o cyklus s daným počtem opakování. Umožňuje provést část kódu, kterou ohraničuje, vícekrát. Vysvětleme si ho na jednoduchém příkladu:

for i in 1 2 3
do
    echo "Řádek č. $i"
done

V tomto případě se příkaz echo provede třikrát. Již jsme si vysvětlili, že příkaz echo vypíše to, co dostane v parametrech (jinými slovy co napíšeme za něj). V tomto konkrétním případě není nutné text uzavírat do uvozovek, ale je dobrým zvykem je i přesto psát (příkaz echo je nevypíše), zvlášť pokud pracujeme s proměnnými (viz předchozí podkapitola článku).

Proměnná je v podstatě pojmenované místo v paměti, kam můžeme uložit nějakou hodnotu. V tomto příkladu se nachází proměnná pojmenovaná „i“, do které příkaz for postupně ukládá hodnoty, které jsou napsané za klíčovým slovem in (při každém průchodu – volání příkazu echo tedy proměnná i obsahuje postupně hodnoty 1, 2 a 3). Možná tedy hádáte správně, že tento malý příklad vypíše toto:

Řádek č. 1
Řádek č. 2
Řádek č. 3

Ostatní části příkazu for jsou pevně dané, přičemž jsou zde důležitá i odřádkování, které ale lze nahradit středníky (díky tomu se celý náš skript dá napsat na jeden řádek, jak jsme viděli v prvním dílu). Jeho zápis tedy začíná slovem „for“, za kterým následuje název proměnné (libovolný, ale doporučuji používat pouze malá písmena anglické abecedy, názvy proměnných obecně mívají různá omezení – nejsem si jistá, jak je to v Bashi, v případě potřeby to můžete vyhledat na internetu). Poté následuje slovo „in“ a jednotlivé hodnoty (typicky čísla či řetezce – text) oddělené mezerami, které se budou postupně do proměnné doplňovat. Na dalším řádku (nebo oddělené středníkem) je slovo „do“ a na posledním řádku (případně opět oddělené středníkem) je slovo „done“. Vše, co je mezi těmito slovy („do“ a „done“) se provede tolikrát, kolik je položek v seznamu za slovem „in“. Mezery či tabulátor před příkazy uvnitř cyklu nejsou povinné, slouží jen k odsazení těchto řádků, aby byl kód přehlednější.

Pokud bychom chtěli napsat cyklus for do jednoho řádku, středník se napíše před klíčová slova „do“ a „done“ – když jsem ho napsala za slovo „do“, příkaz skončil chybou. Volitelně lze napsat pro přehlednost za středník mezeru, ale není povinná. Jednořádková verze příkladu by tedy mohla vypadat například takto:

for i in 1 2 3;do echo "Řádek č. $i";done

Pozn. Existuje více možných variant zápisu (syntaxe) příkazu for, pro pochopení našeho skriptu nám bude stačit tato varianta.

Podmínka: if

Další typický prvek využívaný v programování je podmínka. Slouží k tzv. větvení programu, kdy určitá část programu se provede jen tehdy, když podmínka platí. Volitelně tam může být i část, která se provede naopak jen tehdy, když podmínka neplatí – část programu tak můžeme rozdělit na dvě části, z nichž se vždy podle určitého pravidla vybere jedna, která se použije. Často ale bývá pouze jedna část, která se buď provede nebo ne – tak jako v našem příkladu:

if [ -f "soubor.txt" ]
then
    echo "Soubor s názvem soubor.txt existuje."
fi

Do hranatých závorek za příkaz if patří podmínka, která se vyhodnocuje. Mezery kolem hranatých závorek jsou důležité – ve skutečnosti totiž znak [ funguje jako příkaz test a podmínka je vlastně tvořena jeho přepínači a parametry – tím se ale nyní nemusíme zatěžovat, důležité je znát ten správný zápis.

Složitější varianty větvení pomocí if se sice v našem skriptu nepoužívá, ale pro úplnost je zde uvedu, mohly by se vám hodit. Takhle vypadá větev else, což je část, která se provede tehdy, když podmínka v if neplatí:

if [ -f "soubor.txt" ]
then
    echo "Soubor s názvem soubor.txt existuje."
else
    echo "Soubor s názvem soubor.txt neexistuje."
fi

Je také možné vytvořit více větví s více různými podmínkami pomocí zápisu elif. V tomto případě platí, že se vždy vykoná pouze jedna větev (po splnění jedné podmínky se další podmínky již nevyhodnocují), větev else se vykoná tedy pouze tehdy, když žádná z podmínek neplatí.

if [ -f "soubor.txt" ]
then
    echo "Soubor s názvem soubor.txt existuje."
elif [ -f "soubor2.txt" ]
then
    echo "Soubor s názvem soubor2.txt existuje."
elif [ -f "soubor3.txt" ]
then
    echo "Soubor s názvem soubor3.txt existuje."
else
    echo "Ani jeden ze souborů neexistuje."
fi

Podobně jako u příkazu for, i příkaz if lze zapsat na jeden řádek tak, že dáme středníky před klíčová slova then, else, fi a elif:

if [ -f "soubor.txt" ];then echo "Soubor s názvem soubor.txt existuje.";elif [ -f "soubor2.txt" ];then echo "Soubor s názvem soubor2.txt existuje.";elif [ -f "soubor3.txt" ];then echo "Soubor s názvem soubor3.txt existuje.";else echo "Ani jeden ze souborů neexistuje.";fi

Doufám, že je z tohoto popisu struktura příkazu if zřejmá, zbývá ještě popsat strukturu samotné podmínky. Ve své podstatě to může být libovolný příkaz, který když skončí úspěšně, podmínka je splněna, pokud skončí neúspěšně, podmínka splněna není. Pro potřeby článku se zaměříme na příkaz test, který je reprezentovaný právě hranatými závorkami (ano, můžete napsat if test -f "soubor.txt"; then ... – bude to fungovat, ale přecijen hranaté závorky jsou kratší a přehlednější 😉 ).

Podobně název souboru nemusí být v uvozovkách, problém pak ale nastává u názvů souborů obsahujících mezeru, jak jsem popisovala výše v poznámce pro psaní proměnných. Vnitřek těchto hranatých závorek může mít například jednu z následujících podob:

  • -e soubor – soubor existuje
  • -f soubor – existuje a je to standardní soubor
  • -d soubor – existuje a je to adresář
  • -r soubor – existuje a je čitelný (uživatel má práva pro čtení souboru)
  • -w soubor – existuje a je zapisovatelný (uživatel má práva pro zápis souboru)
  • řetězec1 = řetězec2 – řetězce (texty) jsou shodné, používá se typicky pro kontrolu obsahu proměnné ($promenna = text)
  • řetězec1 != řetězec2 – řetězce nejsou shodné
  • ! výraz – negace (otáčí výsledek výrazu, výraz je jedna z výše uvedených možností)
  • výraz1 -a výraz2 – spojení výrazů a (podmínka platí, když platí první a zároveň druhý výraz)
  • výraz2 -o výraz2 – spojení výrazů nebo (podmínka platí, když platí první nebo druhý výraz)

Příklad: -e "můj soubor.txt" -a $promenna = "můj text"

Negace a spojení výrazů je možno provést i mimo hranaté závorky, vlastně pak jde o spojování příkazů, ale chová se to v postatě stejně. Negace se provádí také znakem !, spojení a pomocí znaků &&, spojení nebo pomocí znaků ||. Ukážeme si to na příkladu přímo z našeho skriptu – tam je totiž použitý právě tento způsob.

if [ -f "$soubor" ] && [ "$soubor" != *. ]

V proměnné s názvem soubor je uložený název souboru. Nejprve se vyhodnotí podmínka v prvních hranatých závorkách a zjistí se, zda soubor existuje (a je to soubor). Pokud je tato podmínka splněna, vyhodnotí se druhá, která zkoumá, jestli název souboru nekončí tečkou. Hvězdička totiž v Bashi obecně znamená výraz pro cokoliv, zápis *. tedy jednoduše řečeno představuje cokoliv, co končí tečkou. Podmínka je tedy splněna, pokud název souboru nekončí tečkou (jelikož je použitý výraz se znaky !=, což znamená nerovná se).

Pokračování příště

Více o hvězdičkách a dalších znacích uvnitř skriptu, které vám možná zatím nedávají moc smysl, si povíme příště. Mezitím si můžete procvičit získané znalosti prakticky přímo v Linuxovém terminálu, možnosti jak se do něj dostat jsem popsala v minulém dílu (pokud se vám nechce nic instalovat a chcete rovnou zkoušet, doporučuji online variantu linuxového terminálu).

Čekání si můžete zkrátit také rozšířením svých znalostí pomocí různých dalších tutoriálů, mým cílem nebylo napsat kompletní příručku Bashe, ale vysvětlení věcí k pochopení našeho třídícího skriptu. 😉 Můžete zkusit třeba kurz Základy Linuxu na IT network nebo Seriál o Bashi na Linux Express. Další návody můžete najít na internetu, když do vyhledávače zadáte třeba „Bash“ nebo „Základy Bashe“.

V příštím dílu se podíváme na zbylé části skriptu, které zatím nebyly vysvětleny a vše si dovystvělíme. 😉