Ke správě souborů nevyhnutelně patří i jejich hledání. Naučení se několika trikům může v budoucnu ušetřit spoustu času.
29.5.2006 06:00 | Jiří Václavík | přečteno 102222×
Hledání souborů patří mezi relativně častou činnost. Téměř veškeré hledání se v Linuxu provádí příkazy find a locate.
find prochází adresářovou strukturou od zadané úrovně a s každým vyhovujícím souborem provádí definovanou akci. Vyhovující je soubor tehdy, splňuje-li všechny zadané podmínky.
Zaměřme se nyní na syntaxi příkazu. Ta obecně vypadá následovně.
$ find [přepínače] [cesty] [výraz]
Jak find prochází adresářovou strukturu, jsou soubory postupně zpracovávány pomocí výrazů. Výrazy určují podmínky pro vyhovující soubory i samotné akce. Právě jimi se bude zabývat značná část tohoto článku.
Výraz začíná na jeden ze znaků !-(,).
Není-li uveden žádný výraz, je význam stejný, jako by zde byl výraz -print, který tiskne jména souborů na standardní výstup. Pomocí -print můžeme vypsat vyhovující jména souborů v určitém adresáři.
Pokud není uvedena cesta, prohledává se aktuální adresář.
Konkrétnější příklad hledá v zadaném adresáři hledaný soubor a výsledky vypisuje.
$ find /zadany/adresar -name hledany_soubor -print
A následující příkaz vypisuje rekurzivně seznam všech souborů aktuálního adresáře.
$ find
Nutno poznamenat, že pro hledání souborů pouze podle názvu je často lepší použít příkaz locate, jež je k tomuto účelu přímo určený.
Výrazy se skládají z operandů a operátorů. Nyní si nejužívanější zástupce z nich představíme. Protože příkaz find má složitější syntaxi, uvedeme si k nim také větší množství příkladů.
Jednotlivé operandy jsou odděleny operátory. Následující tabulka operátorů je seřazena podle priority od nejvyšší k nejnižší.
Operátor | Význam |
( výraz ) | závorky |
! výraz, -not výraz | negace |
výraz výraz, výraz -a výraz, výraz -and výraz | logické A |
výraz -o výraz, výraz -or výraz | logické NEBO |
výraz , výraz | vyhodnotí se oba výrazy, ale použije se jen návratová hodnota druhého |
Operandy tvoří výrazy pro operátory. Můžeme je rozdělit na tři skupiny.
Skupina operandů | Význam | Návratová hodnota |
volby | upřesňují chování příkazu | true |
testy | testují soubor | true nebo false |
akce | provádí nad soubory danou činnost | true nebo false |
Volby upravují činnost příkazu a vždy vracejí pravdivou hodnotu.
Volba | Význam |
-daystart | čas se počítá od 0:00, nikoliv od okamžiku před 24 hodinami, jak je tomu implicitně |
-maxdepth n | nastavuje největší hloubku zanoření do podadresářů |
-mindepth n | nastavuje nejmenší hloubku zanoření do podadresářů (nejsou tedy zpracovávány soubory zanořené níže, než je uvedeno) |
-follow | následuje symbolické odkazy |
-xdev | neprocházejí se adresáře na jiných souborových systémech |
-depth | přednost při zpracování má obsah adresáře před ním samotným |
Pro výpis všech souborů v aktuálním adresáři bez rekurze nastavíme úroveň zanoření na 1.
$ find . -maxdepth 1
Testy jsou hlavní složkou výrazů, která se stará o filtrování souborů. Soubory, jež projdou testy, jsou dále obvykle předávány akcím.
Část testů přijímá číselnou hodnotu. Čísla lze většinou psát trojím způsobem.
Pro lepší představu si ukažme konkrétní příklad. Hledáme-li soubor, který byl naposledy otevřen před 4 dny, zadáme -atime 4. Hledáme-li soubor, který byl naposledy otevřen později, než před 4 dny (před 3, 2 nebo 1 dnem), použijeme volbu -atime -4. Poslední možností je soubor otevřen před více než 4 dny (před 5, 6, 7 nebo více dny) - tedy -atime +4.
Zde je přehled testů příkazu find.
Test | Význam |
-true | pravda |
-false | nepravda |
-name soubor | soubor má daný název, přičemž lze zadávat žolíkové znaky |
-lname soubor | obsah symbolického odkazu odpovídá zadanému jménu, lze opět použít žolíkové znaky |
-amin n | soubor byl otevřen před n minutami. |
-atime n | soubor byl otevřen před n dny. |
-anewer soubor | soubor byl otevřen dříve než uvedený soubor |
-cmin n | i-uzel souboru byl změněn před n minutami. |
-ctime n | i-uzel souboru byl změněn před n dny. |
-cnewer soubor | i-uzel souboru byl změněn dříve než uvedený soubor |
-mmin n | soubor byl změněn před n minutami. |
-mtime n | soubor byl změněn před n dny. |
-newer soubor | soubor byl změněn dříve než uvedený soubor |
-empty | soubor (platí i pro adresář) je prázdný |
-perm práva | soubor má nastavena daná přístupová práva |
-uid uid | soubor má uid |
-gid gid | soubor má gid |
-group skupina | soubor patří skupině |
-user uživatel | soubor patří uživateli |
-regex vzor | jméno souboru s cestou vyhovuje vzoru |
-type typ | testuje soubor na zadaný typ (viz dále) |
-size n[jednotka] | soubor má danou velikost (viz dále) |
-samefile soubor | soubor má tentýž i-uzel jako uvedený soubor |
Argument testu -type může být jedním z následujích znaků.
Jednotka u testu -size může nabývat hodnot b (jednotkou jsou 512 B bloky), w (dvoubajtová slova), c (B), k (KiB), M (MiB) nebo G (GiB).
U testů implicitně záleží na velikosti písma. Chceme-li vyhledávat názvy s libovolnou velikostí, existují pro testy, pro něž to má smysl, i varianty na velikosti nezávislé. Začínají vždy písmenem i. Lze použít testy -iname, -ipath, -iregex a -ilname.
Pro nalezení všech souborů aktuálního adresáře, které obsahují kdekoliv ve svém názvu řetězec 2006, použijeme následující příkaz.
$ find . -name '*2006*'
Chceme-li najít ve svém domovském adresáři všechny dokumenty formátu Open Document (předpokládáme, že mají vždy příponu .odf), zadáme
$ find ~ -name '*.odf'
Dále najdeme soubory v adresáři /data/2006, které byly modifikovány během posledních tří minut. Protože je to interval končící v okamžiku zadání příkazu, zadáme záporné číslo. Pokud bychom předali číslo bez znaménka, získali bychom soubory modifikované přesně před třemi minutami.
$ find /data/2006/ -mmin -3
Nyní budeme naopak hledat soubory, k nimž není zaznamenán přístup již déle než rok.
$ find ~ -atime +365 -print
Najdeme všechny soubory v aktuálním adresáři o velikosti mezi 200 a 300 KiB. Pozor však na případy, kdy má soubor alokováno více místa než skutečně používá. Pro zjištění velikosti souboru použijeme příkaz du.
$ find . -size +200k -size -300k
Najdeme rekurzivně všechny soubory v adresáři /home, jejichž vlastníkem je uživatel uzivatel.
$ find /home -user uzivatel
A nyní uděláme to samé, ale nebudeme hledat rekurzivně. Budeme hledat pouze v adresáři /home, ale už ne v jeho podadresářích. Nastavíme hloubku zanoření na 1.
$ find /home -maxdepth 1 -user uzivatel
Najdeme všechny soubory, jejichž práva mají hodnotu 700.
$ find . -perm 700
Znaménka + a - zde fungují jako operátory A a NEBO. Nyní najdeme ty soubory, které může vlastník číst, spouštět nebo do nich zapisovat.
$ find . -perm +700
A teď ty, které splňují všechny uvedené podmínky.
$ find . -perm -700
Akce | Význam |
tiskne jména vyhovujících souborů, tato akce je nastavená implicitně | |
-fprint soubor | tiskne jména vyhovujících souborů do souboru |
-printf formát | formátování výstupu |
-fprintf soubor formát | stejné jako -printf, ale přesměruje výstup do souboru |
-exec příkaz | provede příkaz, přičemž {} se expanduje do názvu zpracovávaného souboru |
-ok příkaz | stejně jako -exec, ale vyžádá si potvrzení akce |
-ls | výpis informací o souboru |
-print0 | tiskne jména souborů oddělená znakem NUL, což je výhodné v kombinaci s příkazem xargs -0 |
-quit | konec vykonávání príkazu |
Pro podrobný výpis všech souborů, které příkaz find najde, použijeme pomocí volby -exec příkaz ls -l. Každý příkaz musíme ukončit středníkem.
$ find -name '*.odf' -exec /bin/ls -l {} \;
Pro smazání všech souborů v adresáři /tmp, které jsou starší než 30 dní, zadáme následující příkaz. Pokud bychom tento příkaz náhodou chtěli provádět periodicky, použijeme cron.
$ find /tmp/ -atime +30 -exec rm -f {} \;
Pro smazání pouze prázdných souborů v aktuálním adresáři použijeme tento příkaz.
$ find . -empty -type f -exec rm -f {} \;
Dále si necháme určit formát souboru pro každý soubor v zadaném adresáři.
$ find . -type f -exec file '{}' \;
Pomocí akce -printf lze tisknout na výstup řetězec přesně podle zadaných kritérií. Struktura takového příkaz find pak vypadá následovně.
$ find [...] -printf 'formátovací řetězec'
Ve formátovacím řetězci lze kromě obvyklých escape sekvencí použít také zástupné symboly začínající znakem procenta. Některé z nich jsou uvedeny v tabulce, kompletní seznam pak je v manuálové stránce.
Symbol | Význam |
%% | znak % |
%Aznak, %a | čas posledního přístupu |
%Cznak, %c | čas poslední změny i-uzlu |
%Tznak, %t | čas poslední modifikace |
%d | hloubka zanoření v adresářové struktuře |
%P | pouze jméno souboru |
%p | jméno souboru relativně |
%f | jméno souboru bez podadresářů |
%h | cesta k souboru |
%g, %G | jméno, číslo skupiny |
%u, %U | jméno, číslo uživatele |
%m | práva osmičkově |
%H | ten argument příkazového řádku, díky kterému byl soubor nalezen |
%i | číslo i-uzlu |
%n | počet odkazů |
%s | velikost v bajtech |
Znakem u symbolů %A, %C a %T může být jedna z následujících možností.
Znak | Význam |
lokalizovaný čas | |
c | datum a čas |
X | čas |
b, B | datum |
a, A | den v týdnu pevné, volné délky |
b, B | měsíc v roce pevné, volné délky |
obecný čas | |
r | čas 12 hh:mm:ss |
T | čas 24 hh:mm:ss |
@ | počet sekund od 1.1.1970 0:00 GMT |
H, I, k, l | hodina 00-23, 01-12, 0-23, 1-12 |
M | minuta 00-59 |
S | sekunda 00-59 |
p | AM/PM |
w | den v týdnu 0-6 |
m | měsíc v roce 0-12 |
j | den v roce 001-366 |
y, Y | rok yy, YYYY |
W | týden pondělí-neděle 00-53 |
Po spuštění následujícího příkazu se zobrazí jména a čas poslední modifikace souboru podle dané šablony.
$ find . -printf 'Soubor %f naposledy změněn v %Ac\n'
Výstup lze formátovat stejně jako céčkovskou funkci printf(3), takže pro přehledný výstup ve sloupcích je možný například tento zápis.
$ find . -printf '%-20f %u (%g)\n'
A nakonec, pro naformátování všech souborů aktuálního adresáře (ty, které zastupuje hvězdička) jako SQL příkazu, zadáme toto.
$ find * -prune -printf "INSERT INTO soubory (jmeno, podadresar, velikost) VALUES ('%f', '%h', %s);\n"
Příkaz xargs provádí akci nad soubory, jejichž jména jsou této funkci předána rourou jako parametry. Perfektně se doplňuje s příkazem find.
xargs je užitečný například tehdy, nestačí-li nám délka řádku shellu.
Typickým příkladem užití příkazu xargs je hledání všech souborů vyhovujících vzoru, které navíc obsahují dané slovo. Následující příkaz najde v adresáři /etc všechny .conf soubory, v nichž se vyskytuje slovo cgi.
$ find /etc -name '*.conf' | xargs grep -li cgi
Poznámka - Teoreticky by mohl fungovat i tento příkaz, avšak selhal by při překročení maximální dělky příkazu v shellu.
$ grep -li cgi `find /etc -name '*.conf'`
Zpracováváme-li soubory s nestandardními jmény, jistě přijde vhod přepínač -0. Ten totiž čte argumenty, jež nejsou odděleny bílým místem, ale znakem NUL. V této souvislosti si vzpomeňme na akci -print0 příkazu find. Tato akce vytváří vstup formátovaný právě pro xargs -0.
Pokud tedy xargs používáme ve spolupráci s find, vždy bychom měli oddělovat argumenty znakem NUL, abychom se vyhnuli případným nepříjemnostem. Poslední příkaz na základě této myšlenky přepíšeme.
$ find /etc -name '*.conf' -print0 | xargs -0 grep -li cgi
A nakonec napíšeme příkaz, který v aktuálním adresáři smaže soubory končící příponou .backup.
$ find . -name '*.backup' -print0 | xargs -0 /bin/rm -f
Příkaz locate hledá zadaný vzor v databázi jmen souborů. Právě díky databázi je výrazně rychlejší než find. Nevýhodou je, že databáze nemusí být vždy aktuální a je třeba ji pravidelně obnovovat.
Pokud zadáme příkaz locate se vzorem jako jediným argumentem, potom budou na výstup vypsány všechny soubory, jež obsahují tento vzor.
$ locate vzor
Mezi přepínače, které lze u příkazu locate použít patří tyto.
Přepínač | Význam |
-d databáze | použije se uvedená databáze namísto implicitní |
-i | ignoruje velikost písmen |
-c | vypisuje pouze počet výsledků |
-S | vypisuje statistiky |
-r, --regex | vzor se vyhodnocuje jako regulární výraz |
Implicitně databázi máte pravděpodobně v /var/lib/locatedb nebo /var/spool/locate/locatedb. Pokud ani na jednom z těchto umístění není, můžete ji najít pomocí locate.
Databáze se obnovuje příkazem updatedb bez nebo s argumenty. Pomocí voleb --localpaths a --netpaths lze uvést konkrétní cesty, jež se mají prohledávat. --prunepaths naopak konkrétní cesty vynechává, což lze použít například na dočasně připojené oddíly. Argumentem pro tyto volby je řetězec 'cesta1 cesta2 ...'. Pro výstup do konkrétní databáze použijeme --output.
Mimoto by nás možná mohl zajímat program rlocate, který zajišťuje, že je databáze jmen souborů aktualizována automaticky při změnách.
Nástroje lsof a fuser hledají právě otevřené soubory. Zejména lsof má velké množství všemožných podmínek. Všechny otevřené soubory zjistíme příkazem
$ lsof
Nyní najdeme pouze ty otevřené soubory, které používá aplikace s PID 5687.
$ lsof -p 5687
Pro podrobnější informace o lsof lze nahlédnout do jednoho ze článků uvedených níže.