Linux: Kniha kouzel, vanilková příchuť 2.14 (15. července 2022)
Veškerá moc příkazové řádky/příkazového řádku přehledně, pro začátečníky i pokročilé

6. Hledání souborů

Řada 2.x vanilkové příchuti Linuxu: Knihy kouzel je od 15. července 2022 do 1. března 2025 ve stavu dlouhodobé pasivní údržby; nahlášené chyby budou opravovány, ale aktivní vývoj se již věnuje jiným projektům. Máte-li zájem pokračovat v tvorbě Linuxu: Knihy kouzel pro novější verze linuxových operačních systémů, kontaktujte autora nebo rovnou vytvořte odnož.

1. Úvod

Tato kapitola se zabývá vyhledáváním adresářových položek (souborů a adresářů). Převážně se zabývá příkazem „find“, který strukturu adresářů skutečně prochází a prohledává, ale zahrnuje také vyhledávání spustitelných souborů (programů) a na databázi založený příkaz „locate“.

GNU Findutils, jejichž součástí je příkaz „find“, jsou vyvíjeny v rámci projektu GNU.

2. Definice

  • Výchozí bod je cesta (relativní či absolutní) zadaná příkazu „find“, ze které tento příkaz zahajuje vyhledávání. Může být absolutní i relativní. Nejčastěji se jedná o adresář (např. „.“), ale může jít i o soubor či symbolický odkaz na adresář či soubor. Příkaz find výchozí bod nezkracuje, vždy ho zpracovává tak, jak je zadán.
  • Hloubka je celé číslo, které vyjadřuje počet adresářů od výchozího bodu k právě testované adresářové položce. Hloubku 0 mají pouze výchozí body; hloubku 1 soubory a podadresáře v nich, hloubku 2 ty další atd. Je-li např. „/usr/share“ výchozí bod, pak adresář „/usr/share“ má hloubku 0, soubor „/usr/share/.lock“ by měl hloubku 1, soubor „/usr/share/test/copyright.gz“ hloubku 2 atd.
  • Průchod adresářovou strukturou může být do šířky (výchozí stav – každý adresář je nejprve zpracován sám o sobě (provedou se nad ním testy a v případě úspěchu se vykonají akce) a teprve poté do něj find vstoupí a prozkoumá jeho obsah) nebo do hloubky (v tom případě find pokaždé nejprve vstoupí do adresáře a zpracuje veškerý jeho obsah a teprve „na odchodu“ zpracuje i samotný adresář). Výchozí je průchod do šířky. Průchod do hloubky se aplikuje pouze tehdy, je-li zadán globální parametr „-depth“ nebo je-li použita akce „-delete“. Při průchodu do hloubky nelze použít akci „-prune“.
  • Názvem položky se u příkazu „find“ rozumí samotný název adresářové položky (bez cesty).
  • Cestou položky se u příkazu „find“ rozumí výchozí bod (tak, jak byl zadaný) a za ním adresářová cesta k položce včetně jejího názvu. Je-li např. výchozí bod „.“, je cestou položky např. „./test.sh“.

3. Zaklínadla: find: testy

3/1 Typ adresářové položky (soubor, adresář, odkaz...)

@obyčejný soubor#1
-type f
@adresář#2
-type d
@symbolický odkaz (jakýkoliv/na soubor/na adresář/absolutní/relativní)#3
-type l
-type l -xtype f
-type l -xtype d
-lname "/*"
-type l ! -lname "/*"
@speciální zařízení (blokové/znakové/jakékoliv)#4
-type b
-type c
-type b,c
@pojmenovaná roura#5
-type p
@soket#6
-type s

3/2 Název položky a cesta

Poznámka: Písmeno „i“ v následujících parametrech vypne rozlišování mezi velkými a malými písmeny.

@název položky#1
-[i]name "vzorek"
@cesta položky#2
-[i]path "vzorek"
@cesta položky se shoduje s regulárním výrazem#3
[-regextype posix-extended] -[i]regex 'regulární výraz'
@vyloučit z prohledávání podadresáře, jejichž cesta položky se shoduje s regulárním výrazem/odpovídá mu#4
\( [-regextype posix-extended] ! -[i]regex 'regulární výraz' -o -prune -false \) zbytek testů
\( -regextype posix-extended ! -[i]regex '.*(regulární výraz).*' -o -prune -false \) zbytek testů
@hodnota symbolického odkazu#5
-[i]lname "vzorek"

3/3 Velikost souboru

@M až N gibibajtů/mebibajtů/kibibajtů/bajtů#1
-size +$((M*2**30-1))c -size -$((N*2**30+1))c
-size +$((M*2**20-1))c -size -$((N*2**20+1))c
-size +$((M*2**10-1))c -size -$((N*2**10+1))c
-size +$((M-1))c -size -$((N+1))c
@M až N gigabajtů/megabajtů/kilobajtů/bajtů#2
-size +$((M*10**9-1))c -size -$((N*10**9+1))c
-size +$((M*10**6-1))c -size -$((N*10**6+1))c
-size +$((M*10**3-1))c -size -$((N*10**3+1))c
-size +$((M-1))c -size -$((N+1))c
@minimálně N gibibajtů/mebibajtů/kibibajtů/bajtů#3
-size +$((N*2**30-1))c
-size +$((N*2**20-1))c
-size +$((N*2**10-1))c
-size +$((N-1))c
@maximálně N gibibajtů/mebibajtů/kibibajtů/bajtů#4
-size -$((N*2**30+1))c
-size -$((N*2**20+1))c
-size -$((N*2**10+1))c
-size -$((N+1))c
@minimálně N gigabajtů/megabajtů/kilobajtů#5
-size +$((N*10**9-1))c
-size +$((N*10**6-1))c
-size +$((N*10**3-1))c
-size +$((N-1))c
@maximálně N gigabajtů/megabajtů/kilobajtů#6
-size -$((N*10**9+1))c
-size -$((N*10**6+1))c
-size -$((N*10**3+1))c
-size -$((N+1))c
@přesně N gibibajtů/mebibajtů/kibibajtů/bajtů#7
-size $((N*2**30))c
-size $((N*2**20))c
-size $((N*2**10))c
-size Nc
@přesně N gigabajtů/megabajtů/kilobajtů/bajtů#8
-size $((N*10**9))c
-size $((N*10**6))c
-size $((N*10**3))c
-size Nc
@prázdný soubor#9
-size 0

3/4 Čas („změněno“, „čteno“)

@změněno/čteno během posledních N minut#1
-mmin -N
-amin -N
@změněno/čteno během posledních N dnů (počítaje i dnešek; N≥1)#2
-newermt "$(date -d "N days ago" +%F) 23:59:59.999999999"
-newerat "$(date -d "N days ago" +%F) 23:59:59.999999999"
@změněno/čteno od určitého času#3 (1)
-newermt "čas"
-newerat "čas"
@změněno od 1. ledna 2020 (příklad)#4
-newermt "2019-12-31 23:59:59.999999999"
@změněno/čteno v rozsahu dnů#5 (2)
-newermt "první-den-intervalu 00:00:00" ! -newermt "poslední-den-intervalu 23:59:59.999999999"
-newerat "první-den-intervalu 00:00:00" ! -newerat "poslední-den-intervalu 23:59:59.999999999"
@čteno od poslední změny#6

3/5 Operátory testů

@oba testy musejí být splněny (a také)#1
test1 test2
@test nesmí být splněn (ne-)#2
! test
@závorky (seskupení testů a akcí)#3
\( testy a akce \)
@některý z testů musí být splněn (nebo)#4
test1 -o test2

3/6 Vlastnictví a skupina položky

@vlastník souboru (názvem/UID)#1
-user uživatel
-uid UID
@skupina (názvem/GID)#2
-group skupina
-gid GID
@vlastník nebo skupina souboru neexistuje#3
\( -nogroup -o -nouser \)

3/7 Přístupová práva

@soubor je přístupný pro čtení#1
-readable
@soubor je přístupný pro zápis#2
-writable
@soubor je fakticky spustitelný#3
-executable
@vlastník (u), skupina (g) či ostatní (o) mají právo (jedno z r, w, x)#4
-perm /kdo=právo
! -perm /kdo=právo
@všichni mají určité právo#5
-perm -ugo=právo
@vlastník (u), skupina (g) či ostatní (o) mají všechna práva#6
-perm -kdo=rwx
@mód je přesně ABCD#7
-perm ABCD
@kdokoliv má určité právo#8
@nikdo nemá právo „w“#9

3/8 Obsah souboru

@některý řádek obsahuje/žádný řádek neobsahuje shodu s regulárním výrazem#1
! ( -type d -o ( -type l -xtype d ) ) -readable -exec egrep -q [--] 'regulární výraz' \;
! ( -type d -o ( -type l -xtype d ) ) -readable ! -exec egrep -q [--] 'regulární výraz' \;
@některá řádka obsahuje/žádná řádka neobsahuje podřetězec#2
! ( -type d -o ( -type l -xtype d ) ) -readable -exec fgrep -q [--] 'podřetězec' \;
! ( -type d -o ( -type l -xtype d ) ) -readable ! -exec fgrep -q [--] 'podřetězec' \;

3/9 Zvláštní příznaky a datové položky

@má/nemá příznak „u+s“#1
-perm /u=s
! -perm /u=s
@má příznak „g+s“#2
-perm /g=s
@má příznak omezení smazání „+t“#3
-perm /1000
@ma určitý zvláštní příznak#4
@má alespoň jednu uživatelskou datovou položku#5
@má určitou uživatelskou datovou položku#6

3/10 Velikost adresáře

@prázdný adresář#1
-type d -empty
@adresář s alespoň N položkami/právě N položkami/nejvýše N položkami#2
-type d -exec sh -c 'test $(ls -1AbU -- "$1" | wc -l) -ge N' -- '{}' \; [--print]
-type d -exec sh -c 'test $(ls -1AbU -- "$1" | wc -l) -eq N' -- '{}' \; [--print]
-type d -exec sh -c 'test $(ls -1AbU -- "$1" | wc -l) -le N' -- '{}' \; [--print]

3/11 Ostatní

@pevné odkazy konkrétního souboru#1
-samefile soubor
@neplatné symbolické odkazy#2
-type l -xtype l
@počet pevných odkazů (přesně/minimálně/maximálně N)#3
-links N
-links +$((N-1))
-links -$((N+1))
@test, který vždy uspěje/selže#4
-true
-false
@typ souborového systému#5 (3)
-fstype typ
@čislo i-uzlu#6
-inum číslo
@hloubka prohledávání (řešení 1/řešení 2; viz poznámku)#7 (4)
-mindepth N -maxdepth N
-regextype posix-extended -regex "[^/]*(/[^/]*){$((N+počet-lomítek-v-cíli))}" [-prune]

4. Zaklínadla: find: akce

4/1 Vypsat údaje

@cestu položky na standardní výstup (txt/txtz)#1 (5)
-print
-print0
@vypsat údaje podle formátu na standardní výstup/do souboru#2
-printf 'formát'
-fprintf soubor 'formát'
@zapsat cestu položky jako záznam do souboru (txt/txtz)#3
-fprint soubor
-fprint0 soubor

4/2 Ostatní akce

@spustit příkaz po dávkách (vždy uspěje)#1 (6)
-exec příkaz [parametry příkazu] '{}' +
@spustit příkaz pro každou položku (s cestou/bez cesty)#2 (7)
-exec příkaz [parametry příkazu] \;
-execdir příkaz [parametry příkazu] \;
@smazat soubor či prázdný adresář#3 (8)
-delete
@je-li položka adresář, nevstupovat do něj a ignorovat jeho obsah#4 (9)
-prune
@smazat soubor či jakýkoliv (i neprázdný) adresář#5 (10)
-exec rm -R[v] [--] '{}' \; -prune
@ukončit načítání dalších položek#6
-quit
@spustit příkaz po dávkách po adresářích (vždy uspěje; nedoporučuji)#7 (11)(12)
-execdir příkaz [parametry příkazu] '{}' +

5. Zaklínadla: Akce -printf a -fprintf

@název položky/cesta položky/cesta položky bez výchozího bodu#1
%f⊨ test.txt
%p⊨ ./testdir/test.txt
%P⊨ testdir/test.txt
@typ adresářové položky (písmeno)#2
%y⊨ f
@cesta položky bez názvu#3
%h⊨ ./testdir
@přístupová práva symbolicky/číselně#4
%M⊨ -rw-r--r--
%#m⊨ 0644
@vlastník (jméno/UID)#5
%u⊨ milada
%U⊨ 1000
@skupina (jméno/GID)#6
%g⊨ milada
%G⊨ 1000
@velikost souboru v bajtech#7
%s⊨ 3
@čas „změněno“(normálně/časová známka Unixu)#8 (13)
%TY-%Tm-%Td %TT⊨ 2020-03-13 22:03:00.9467889490
%T@⊨ 1584133380.9467889490
@čas posledního přístupu (normálně/časová známka Unixu)#9 (14)
%AY-%Am-%Ad %AT⊨ 2020-03-13 22:03:00.9467889490
%A@⊨ 1584133380.9467889490
@počet pevných odkazů#10
%n⊨ 1
@výchozí bod#11
%H⊨ .
@hloubka prohledávání#12
%d⊨ 1
@typ souborového systému#13
%F⊨ ext4
@cíl (obsah) symbolického odkazu#14 (15)
%l
@číslo „inode“#15
%i⊨ 2758499

6. Zaklínadla: Celé příkazy

6/1 Hledání textových souborů podle obsahu

@najít soubory, jejichž některá řádka obsahuje/žádný řádek neobsahuje shodu s regulárním výrazem#1
find kde -type f -readable [další podmínky] -exec egrep -l 'regulární výraz' '{}' +
find kde -type f -readable [další podmínky] -exec egrep -L 'regulární výraz' '{}' +
@najít soubory, jejichž některá řádka obsahuje/žádný řádek neobsahuje podřetězec#2
find kde -type f -readable [další podmínky] -exec fgrep -l 'regulární výraz' '{}' +
find kde -type f -readable [další podmínky] -exec fgrep -L 'regulární výraz' '{}' +
@najít symbolické smyčky, tzn. symbolické odkazy, které přímo či nepřímo odkazují samy na sebe#3
find kde -type l -printf '%Y%p\0' | sed -zE 's/^L//;t;d' [| tr \\0 \\n]

6/2 Hledání programů

@najít úplnou cestu podle názvu příkazu#1 (16)
which název-příkazu
@najít manuálové stránky (jako soubory/název a sekce)#2
find -L /usr/share/man -type f -name 'název.*.gz' [| sort]
find -L /usr/share/man -type f -name 'název.*.gz' | sed -E 's/.*\/(.*)\.([^.]+)\.gz$/\1(\2)/' | sort -u

6/3 Obecné hledání

@najít a odstranit prázdné adresáře/soubory/soubory i adresáře#1
find kde -type d -empty -delete
find kde -type f -empty -delete
find kde -empty -delete
@najít neplatné symbolické odkazy (pro člověka/pro skript)#2 (17)
[sudo] find kde -type d -exec symlinks '{}' + | egrep '^dangling: '
[sudo] find kde -type l -xtype l [-print0]
@najít adresářové položky, jejichž název/celá cesta obsahují shodu s regulárním výrazem (pomoc databáze „mlocate“)#3
[sudo] locate --regex -b [-e] [-i] -r 'regulární výraz pro název položky'
[sudo] locate --regex [-e] [-i] -r 'regulární výraz pro celou cestu'

7. Parametry příkazů

7/1 find

[sudo] find [-P] cesta globální parametry testy-a-akce
[sudo] find -L cesta globální parametry testy-a-akce
[sudo] find -H cesta globální parametry testy-a-akce

Chování k symbolickým odkazům: -P: nikdy nenásledovat (interpretovat každý odkaz jen jako adresářovou položku); -L: vždy následovat (chovat se, jako by na místě symbolického odkazu byl odkazovaný soubor, adresář apod.; vstupovat do takových adresářů); -H: následovat jen symbolické odkazy, které jsou přímo uvedeny jako „cesta“ na příkazové řádce.

Globální parametry:

☐ -xdevPo každou „cestu“ na příkazové řádce omezí prohledávání jen na jeden souborový systém.
☐ -depthAdresář zpracuje až „na odchodu“, tzn. teprve po zpracování veškerého jeho obsahu. Normálně se adresář zpracuje jako první a pak se teprve testuje jeho obsah.
☐ -maxdepth čísloSestoupí maximálně do uvedené hloubky. 0 znamená testovat jen cesty uvedené na příkazové řádce; 1 znamená testovat i položky v adresářích uvedených na příkazovém řádku; 2 znamená testovat i položky v podadresářích těchto adresářů atd.
☐ -mindepth čísloNa položky v hloubce nižší než „číslo“ se nebudou aplikovat žádné testy ani akce a nebudou vypsány. Pozor, to znamená, že na ně nebude účinkovat akce -prune! Příkaz find se pokusí vstoupit do každého adresáře s hloubkou menší než „mindepth“.

Testy a akce jsou uvedeny jako zaklínadla v předchozí části kapitoly.

8. Instalace na Ubuntu

Většina uvedených příkazů je základními součástmi Ubuntu. Pouze příkaz „symlinks“ musíte v případě potřeby doinstalovat:

sudo apt-get install symlinks

9. Tipy a zkušenosti

  • Naučte se s příkazem „find“ používat akci „-exec“, zejména její hromadnou variantu „po dávkách“. Její použití je většinou mnohem pohodlnější než tradiční kombinace s příkazem „xargs“. Jedinou výjimkou je případ, kdy chcete příkazy vykonávat paralelně.
  • Příkaz „find“ cesty na svém výstupu nijak neřadí, ale při průchodu do šířky (což je výchozí) „zpracuje“ adresář dřív, než do něj vstoupí a prohledá jeho obsah. Při průchodu do hloubky naopak zpracuje adresář až po zpracování celého podstromu uvnitř něj.
  • Příkaz „locate“ respektuje přístupová práva a najde pouze adresářové položky, ke kterým má uživatel v dané chvíli přístup.
  • Nepoužívejte akci „-execdir“. Je pomalá při spouštění akce v mnoha adresářích a odmítne pracovat, pokud bude v proměnné prostředí PATH relativní cesta nebo závěrečná dvojtečka.

10. Další zdroje informací

11. Zákulisí kapitoly

V této verzi kapitoly chybí:

  • nic

Tato kapitola záměrně nepokrývá:

  • nic
1 Počítadlo nanosekund je v čase volitelné. Podmínkám vyhovují položky, jejichž čas modifikace či čtení je větší než hodnota uvedená v parametru!
2 Dny zadejte ve formátu %F (YYYY-MM-DD).
3 Např. „ext4“, „tmpfs“, „ntfs“, „vfat“.
4 Řešení 1 využívá globálních parametrů, které omezují celé prohledávání a nemohou být složitěji kombinovány s ostatními testy (např. parametr „-mindepth“ způsobí, že na položky s nižší hloubkou testy vůbec nebudou zavolány, což znamená, že se u nich např. neuplatní „-prune“). Řešení 2 pak není použitelné, pokud find prohledává více cílů, které ve své definici obsahují různý počet lomítek.
5 Znaky takto vypsané cesty nejsou nijak odzvláštněny, což vadí jen v případě, že použijete „-print“ a cesta obsahuje znak konce řádky. Pokud s takovým vzácným případem chcete počítat, použijte místo toho „-print0“.
6 Tato varianta je prakticky pohodlnějším ekvivalentem volání příkazu xargs. Použije co největší dávky. Vždy uspěje.
7 Každý výskyt řetězce „{}“ v parametrech příkazu bude při volání nahrazen: v případě první varianty cestou testované položky od výchozího bodu, v případě varianty bez cesty jen názvem souboru s cestou „./“ (příkaz bude spuštěn ve stejném adresáři, kde se položka nachází). Akce uspěje, pokud uspěje příkaz.
8 Akce uspěje, pokud se soubor či adresář podaří smazat.
9 Tato akce funguje jen při průchodu do šířky. Proto lze použít jen tehdy, pokud v celém příkazu není použity ani jeden z paramterů „-delete“ a „-depth“.
10 Tato akce funguje jen při průchodu do šířky. Proto lze použít jen tehdy, pokud v celém příkazu není použity ani jeden z paramterů „-delete“ a „-depth“.
11 Tato varianta vždy uspěje. Shromáždí položky z jednotlivého adresáře a po velkých dávkách (obvykle najednou) je předá ke zpracování uvedenému příkazu. Příkaz se spouští v adresáři, kde jsou vyhledané položky, a dostává pouze název položky s cestou „./“.
12 Pozor! „-execdir“ skončí kritickou chybou, pokud proměnná prostředí PATH obsahuje aktuální adresář, relativní cestu nebo závěrečnou dvojtečku. Důvodem je, že by se v takovém případě mohl příkaz k vykonání vyhledat v různých adresářích různě.
13 V obou případech se bohužel vypíše s desetinnou částí.
14 V obou případech se bohužel vypíše s desetinnou částí.
15 Pokud položka není symbolický odkaz, %l vypisuje prázdný řetězec.
16 Poznámka: tento příkaz (pochopitelně) ignoruje vestavěné příkazy bashe, aliasy, funkce apod. Řídí se pouze proměnnou prostředí PATH.
17 Pozor! Příkaz nemůže správně určit, zda je symbolický odkaz neplatný, pokud nemá přístupová práva k adresáři, na který symbolický odkaz odkazuje! Příkaz „symlinks“ v takovém případě hlásí odkaz jako neplatný, uvedená podoba příkazu „find“ vypíše chybové hlášení „Permission denied“, ale odkaz do seznamu neplatných nezahrne.
Líbí se vám tento projekt a chcete, aby byl ještě lepší? Můžete mi s tím pomoci. Zmiňte se o něm technicky zdatným přátelům, opravte překlepy a nahlašte nefunkční zaklínadla, aby mohla být opravena; poskytněte mi zpětnou vazbu nebo se zapojte do vývoje nových kapitol. Další informace na GitHubu v dokumentu Jak se zapojit.
[BY-SA]

Veškerý obsah této stránky (text, obrázky, zdrojový kód) je možno upravovat a šířit pod podmínkami licence Creative Commons Attribution-ShareAlike 4.0 International. Upozorňuji, že uvedená licence vyžaduje uvedení seznamu autorů, licence a zdroje a poskytnutí stejné či kompatibilní licence k provedeným změnám, jsou-li nějaké. Příslušné údaje jsou dostupné na stránce „Přehled autorů“. Šíření obsahu bez těchto údajů nebo šíření upravené verze bez poskytnutí adekvátní licence k provedeným úpravám je pravděpodobně porušení licenčních podmínek a může být postihováno. Poskytování zdrojového kódu při šíření není touto licencí vyžadováno.

Pro nové verze, další informace, aktuální zdrojový kód a možnost se zapojit do projektu „Linux: Kniha kouzel“ navštivte jeho repozitář na GitHubu.