Hrátky z řádky: find to najde

14. 4. 2008
Doba čtení: 4 minuty

Sdílet

Opět se setkáváme u pravidelné pondělní dávky tipů a triků z černé řádky. Dnes se vracíme k hledání. Celý díl bude věnován příkazu find. Ten je velmi mocný, jak dále uvidíte, umožní nám víc než jen hledání. Find prochází adresářovou strukturu a dokáže nad soubory provádět různé užitečné akce.

Find má mnoho parametrů. Zpočátku se vám bude zdát, že zápis moc nedává smysl a že si ho nikdy nezapamatujete. Naštěstí proto tu máme manuálovou stránku.

Struktura příkazu vypadá následovně:
find cesta výraz

Cesta je jasná – od tohoto adresáře níže se bude hledat. U některých verzí find můžeme cestu vynechat a použije se aktuální. U výrazu je to složitější. Sem spadají volby, které obecně ovlivňují hledání, pomocí nich například můžeme říct jen od/do které úrovně (mindepth, maxdepth) hledat, zda procházet symbolické odkazy (follow), … Dále zde máme testy – ty už se týkají hledaných objektů. Z často používaných je to např. typ (adresář, soubor, link, …), vlastník (user, group). Další důležitou možností jsou tzv. akce (exec, ok). Ty nám umožní spustit libovolný příkaz systému a jako parametr mu předat nalezený soubor. Nakonec tu máme operátory sloužící ke skládání výrazů. Jak každá část výrazu vrací hodnoty a jak probíhá vlastní zpracování je pěkně popsané v Linux – dokumentační projekt, kapitola 11. Tam odkazuji zvídavé čtenáře. My se zaměříme na praktické ukázky.

Úplný základ – hledá soubor s názvem soubor.txt v aktuálním adresáři a podadresářích:

$ find . -name soubor.txt

Vyhledá na disku všechny adresáře s názvem pokus (můžeme použít např. -type f pro soubory, -type l pro linky):

$ find / -type d -name pokus

Můžeme použít i zástupného znaku ? a *. Budeme hledat v domovském adresáři soubory s příponou txt, začínající postup_ (postup1.txt, postup_aaa.txt, …). Musíme zápis ošetřit, aby nebyl interpretován shellem:

$ find ~ -name postup_\*.txt

nebo

$ find ~ -name "postup_*.txt"

Pokud chceme vyhledat v aktuálním adresáři podadresáře a ty už dál rekurzivně neprocházet:

$ find . -type d -maxdepth 1

Je možné třeba i negovat podmínku, v domovském adresáři hledáme všechny soubory, co nemají příponu txt:

$ find ~ -type f \! -name \*.txt

Zajímavé je i hledání podle časů a velikostí souborů. To co napíšu teď se může trochu lišit v různých verzích findu, proto si pročtěte manuál.

Tento příklad najde v domovském adresáři vše co bylo modifikováno v posledních sedmi dnech:

$ find ~ -mtime -7d

Vše co bylo změněno před více než sedmi dny:

$ find ~ -mtime +7d

Přesně před sedmi dny:

$ find ~ -mtime 7d

Je možné použít písmenka s, m, h, d, w pro vteřiny, minuty, hodiny, dny a týdny. Stejně se dá hledat i podle velikostí, na to je parametr -size. -size +100k znamená větší než 100 kilobajtů, -100k menší než 100 kilobajtů a -size 100k je rovno. Pokud nezadáme k, bude výchozí hodnotou 512 bajtový blok. Pokud chceme podle bajtů, přidejte za číslo písmenko  b.

Vyhledá nám v aktuálním adresáři všechny soubory, které byly změněny dřív než soubor:

$ find . -newer soubor

Tento zápis už tak jednoduše nevypadá, přidáme navíc akci exec. Prázdné složené závorky ( {}) budou nahrazeny cestou k nalezenému souboru. Vykoná se příkaz ls -al s touto cestou. Zpětné lomítko před středníkem je důležité. Tím findu říkáme, kde končí příkaz, který chceme vykonávat. Ale středník je i znak, kterým se běžně oddělují příkazy. Při spuštění by se středník chápal jako oddělovač příkazů. Proto jej musíme ošetřit znakem \ nebo zapsat jako ";". Příklad najde v akt. adresáři a podadresářích všechny soubory a vypíše o nich detaily:

$ find . -type f -exec ls -al {} \;

Předchozí příklad nám ukazuje sílu findu. Toto nám smaže všechny soubory s příponou .old v současném adresáři a všech podadresářích:

$ find . -type f -name \*.old -exec rm {} \;

Pomocí findu můžete dělat i poměrně velké zásahy, takhle například změníte vlastnictví souborů jednoho uživatele na druhého:

$ find . -user user1 -exec chown user2 {} \;

Pokud si u -exec nejste jistí, že chcete třeba smazat vše co nám find našel, místo -exec použijte -ok. Před každým vykonáním se find zeptá, jestli akci opravdu provést.

Find má mnohem více možností, projděte si manuál, na různých webech věnujících se find najdete spoustu ukázek a praktických příkladů.

Někdy se můžeme setkat s využitím programu xargs. Ten čte v našem případě ze standardního vstupu jména souborů a vykonává nad nimi zadanou akci (v našem případě mazání):

$ find / -name "*.bak" | xargs /bin/rm -f

nebo

bitcoin_skoleni

$ find / -name "*.bak" -print0 | xargs -0 /bin/rm -f

Vykonají stejnou funkci jako

$ find / -name "*.bak" -exec rm -f {} \;

V prvním příkladu chápe xargs jako oddělovače mezery a znak nového řádku, a to může být problém, např. když v názvu souboru bude mezera. V druhém případě oddělujeme jednotlivé soubory znakem NULL a mezera nás nemůže zaskočit. V případě bez xargs se rm spustí pro každý nalezený soubor, v tu chvíli find čeká na výsledek mazání. Když použijeme xargs, nalezené soubory určené k mazání se předávají xargs a ten po dosažení limitu (5000, ale můžete jej i změnit) spustí jedenkrát rm ( rm -f soubor1 soubor2 ...). Výsledek je potom o dost rychlejší. Ke xargs se ještě určitě vrátíme v dalších dílech.

Autor článku

Petr Macek studoval aplikovanou informatiku na Jihočeské univerzitě, pracuje jako síťový specialista ve firmě Kostax, s. r. o. Baví ho především FreeBSD, sítě a monitoring Cacti.