Zjistil jsem, že neustále hledám syntaxi
find . -name "FILENAME" -exec rm {} \;
hlavně proto, že nechápu, jak přesně část -exec
funguje. Jaký je význam složených závorek, zpětného lomítka a středníku? Existují další případy použití pro ta syntaxe?
Komentáře
Odpověď
Tato odpověď přichází v následujících částech:
- Základní použití
-exec
- Použití
-exec
v kombinaci ssh -c
- pomocí
-exec ... {} +
- pomocí
-execdir
Základní použití -exec
Možnost -exec
přebírá externí nástroj s volitelnými argumenty jako jeho argument a provede jej.
Pokud je řetězec {}
přítomen kdekoli v daném příkazu, každá jeho instance bude nahrazena aktuálně zpracovávanou cestou ( např. ./some/path/FILENAME
). Ve většině prostředí není nutné tyto dva znaky {}
citovat.
Příkaz je třeba ukončit ;
, aby find
věděli, kde to končí (protože poté mohou existovat další možnosti s). Chcete-li ;
chránit před shellem, je třeba jej uvést jako \;
nebo ";"
, jinak jej shell uvidí jako konec příkazu find
.
Příklad (\
na konec prvních dvou řádků slouží pouze pro pokračování řádků):
find . -type f -name "*.txt" \ -exec grep -q "hello" {} ";" \ -exec cat {} ";"
Najdete zde všechny běžné soubory (-type f
) jejichž jména odpovídají vzoru *.txt
v aktuálním adresáři nebo pod ním. Poté pomocí grep -q
otestuje, zda se řetězec hello
vyskytuje v některém z nalezených souborů (který neprodukuje žádný výstup, pouze výstup postavení). U souborů, které obsahují řetězec, bude provedeno cat
k odeslání obsahu souboru do terminálu.
Každý -exec
funguje také jako „test“ u cest nalezených find
, stejně jako -type
a -name
ano. Pokud příkaz vrátí stav nulového ukončení (znamenající „úspěch“), je považována další část příkazu find
, jinak find
příkaz pokračuje dalším názvem cesty. Používá se ve výše uvedeném příkladu k vyhledání souborů, které obsahují řetězec hello
, ale k ignorování všech ostatních souborů.
Výše uvedený příklad ilustruje dvě nejběžnější použití případy -exec
:
- jako test k dalšímu omezení hledání.
- Provedení akce s nalezeným cesta (obvykle, ale ne nutně, na konci příkazu
find
).
Použití -exec
v kombinaci s sh -c
Příkaz, který -exec
může provést, je omezen na externí nástroj s volitelnými argumenty.Přímé použití vestavěných funkcí, funkcí, podmíněných položek, kanálů, přesměrování atd. Pomocí -exec
není možné, pokud není zabaleno do něčeho jako sh -c
podřízený shell.
Pokud jsou požadovány funkce bash
, použijte místo iv id = bash -c
„73af3270ae“>
.
sh -c
běží /bin/sh
se skriptem zadaným na příkazovém řádku, následované volitelnými argumenty příkazového řádku k tomuto skriptu.
Jednoduchý příklad použití sh -c
samostatně, bez find
:
sh -c "echo "You gave me $1, thanks!"" sh "apples"
Tím se předají dva argumenty skriptu podřízeného prostředí. Ty budou umístěny do $0
a $1
pro použití skriptu.
-
The string
sh
. To bude k dispozici jako$0
uvnitř skriptu a pokud vnitřní prostředí vydá chybovou zprávu, přidá před něj tento řetězec. -
Argument
apples
je ve skriptu k dispozici jako$1
a kdyby bylo více argumentů, pak by byly k dispozici jako$2
,$3
atd. Byly by také k dispozici v seznamu"$@"
(kromě pro$0
který by nebyl součástí"$@"
).
To je užitečné v kombinaci s -exec
, protože nám umožňuje vytvářet libovolně složité skripty, které působí na názvy cest nalezené find
.
Příklad: Najděte všechny běžné soubory, které mají určitou příponu souboru, a změňte tuto příponu názvu souboru na jinou příponu, kde jsou přípony uchovávány v proměnných:
from=text # Find files that have names like something.text to=txt # Change the .text suffix to .txt find . -type f -name "*.$from" -exec sh -c "mv "$3" "${3%.$1}.$2"" sh "$from" "$to" {} ";"
Uvnitř inte rnal script, $1
by byl řetězec text
, $2
by byl řetězec txt
a $3
by byl jakýkoli název cesty find
, který pro nás našel. Rozšíření parametrů ${3%.$1}
by převzalo cestu a odstranilo z něj příponu .text
.
Nebo pomocí dirname
/ basename
:
find . -type f -name "*.$from" -exec sh -c " mv "$3" "$(dirname "$3")/$(basename "$3" ".$1").$2"" sh "$from" "$to" {} ";"
nebo s přidanými proměnnými v interní skript:
find . -type f -name "*.$from" -exec sh -c " from=$1; to=$2; pathname=$3 mv "$pathname" "$(dirname "$pathname")/$(basename "$pathname" ".$from").$to"" sh "$from" "$to" {} ";"
Všimněte si, že v této poslední variantě jsou proměnné from
a v podřízeném prostředí se liší od proměnných se stejnými názvy v externím skriptu.
Výše uvedený je správný způsob volání libovolného komplexního skriptu z -exec
s find
. Použití find
ve smyčce jako
for pathname in $( find ... ); do
je náchylné k chybám a nedovolené (osobní názor). Rozděluje názvy souborů na mezery v mezerách, vyvolává globování souborů a také nutí prostředí rozšířit úplný výsledek find
ještě před spuštěním první iterace smyčky.
Viz také:
Použití -exec ... {} +
Na konci lze ;
nahradit +
. To způsobí, že find
provede daný příkaz s co největším počtem argumentů (nalezených názvů cest) než jednou pro každou nalezenou cestu. Řetězec {}
musí fungovat těsně před +
, aby to fungovalo .
find . -type f -name "*.txt" \ -exec grep -q "hello" {} ";" \ -exec cat {} +
Zde find
shromáždí výsledné názvy cest a provede cat
na co největším počtu z nich najednou.
find . -type f -name "*.txt" \ -exec grep -q "hello" {} ";" \ -exec mv -t /tmp/files_with_hello/ {} +
Podobně zde bude mv
proveden jako několikrát, jak je to možné. Tento poslední příklad vyžaduje GNU mv
od coreutils (který podporuje možnost -t
).
Použití -exec sh -c ... {} +
je také efektivní způsob, jak smyčkou přes sadu cest s libovolně složitým skriptem.
Základní informace jsou stejné jako při použití -exec sh -c ... {} ";"
, ale skript nyní trvá mnohem delší seznam argumentů. Lze je opakovat opakováním "$@"
uvnitř skriptu.
Náš příklad z poslední části, která mění přípony názvů souborů:
from=text # Find files that have names like something.text to=txt # Change the .text suffix to .txt find . -type f -name "*.$from" -exec sh -c " from=$1; to=$2 shift 2 # remove the first two arguments from the list # because in this case these are *not* pathnames # given to us by find for pathname do # or: for pathname in "$@"; do mv "$pathname" "${pathname%.$from}.$to" done" sh "$from" "$to" {} +
Použití -execdir
Existuje také -execdir
( implementováno většinou find
variant, není to však standardní možnost).
Funguje to jako -exec
s tím rozdílem, že daný příkaz shellu je spuštěn s adresářem nalezené cesty jako jeho aktuální pracovní adresář a {}
bude obsahovat základní název nalezené cesty bez její cesty (ale GNU find
bude stále před základní název ./
, zatímco BSD find
to neudělá).
Příklad:
find . -type f -name "*.txt" \ -execdir mv {} done-texts/{}.done \;
Tím se každý nalezený *.txt
-file přesune do již existujícího done-texts
podadresáře ve stejném adresáři, kde byl soubor nalezeno . Soubor bude také přejmenován přidáním přípony .done
.
To by bylo o něco složitější s -exec
, protože bychom museli získat základní název nalezeného souboru z {}
, abychom vytvořili nový název souboru. Abychom správně našli adresář done-texts
, potřebujeme také název adresáře {}
.
S -execdir
, některé podobné věci se ulehčí.
Odpovídající operace používající -exec
místo -execdir
bude muset použít podřízený shell:
find . -type f -name "*.txt" -exec sh -c " for name do mv "$name" "$( dirname "$name" )/done-texts/$( basename "$name" ).done" done" sh {} +
nebo
find . -type f -name "*.txt" -exec sh -c " for name do mv "$name" "${name%/*}/done-texts/${name##*/}.done" done" sh {} +
Komentáře
-
-exec
vezme program a argumenty a spustí jej; některé příkazy prostředí se skládají pouze z programu a argumentů, ale mnoho nikoli. Příkaz prostředí může zahrnovat přesměrování a propojení;-exec
nelze (i když celýfind
lze přesměrovat). Příkaz prostředí může používat; && if
atd .;-exec
nemůže, i když-a -o
některé dokáže. Příkaz prostředí může být alias nebo funkce prostředí nebo integrovaný;-exec
nelze. Příkaz prostředí může rozbalit vars;-exec
nemůže (i když vnější prostředí, ve kterém je spuštěnfind
). Příkaz prostředí může$(command)
pokaždé nahradit jinak;-exec
nelze. … - Říkám to ‚ sa shell příkaz je zde špatně,
find -exec cmd arg \;
není ‚ T vyvolá prostředí pro interpretaci příkazového řádku prostředí, běžíexeclp("cmd", "arg")
přímo, nikoliexeclp("sh", "-c", "cmd arg")
(pro které shell by nakonec udělal ekvivalentexeclp("cmd", "arg")
pokudcmd
nebyl zabudován). - Můžete objasnit, že vše argumenty
find
po-exec
a až;
nebo+
tvoří příkaz k provedení spolu s jeho argumenty, přičemž každá instance argumentu{}
je nahrazena aktuálním souborem (s;
) a{}
jako poslední argument před nahrazením+
seznamem souborů jako samostatnými argumenty (v{} +
případ). IOW-exec
trvá několik argumentů ukončených;
nebo{}
+
. - @Kusalananda Nechtěl ‚ váš poslední příklad fungovat také s tímto jednodušším příkazem:
find . -type f -name '*.txt' -exec sh -c "mv $1 $(dirname $1)/done-texts/$(basename $1).done" sh {} ';'
? - @Atralb Ano, také by to fungovalo a mělo by stejný účinek jako poslední část kódu, ale místo spuštění
mv
ve smyčce, jednou za nalezený soubor, provedetesh
amv
pro každý nalezený soubor, který bude pro velké množství souborů znatelně pomalejší.
man
čte název_služby nebo argument obsahující pouze dva znaky “ {} “ bude nahrazen d aktuálním názvem cesty , který se mi zdá dostatečný. Navíc má příklad s-exec rm {} \;
, stejně jako ve vaší otázce. V mých dobách nebyly téměř žádné jiné zdroje než “ velká šedá zeď „, knihy tištěnýchman
stránky (papír byl příjemnější než skladování). Takže vím, že to stačí pro někoho nového v tomto tématu. Tvoje poslední otázka je ale spravedlivá. Bohužel ani @Kusalananda, ani já na to nemáme odpověď.