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

  • @Philippos: Vidím váš názor. Pamatujte, že manuálové stránky jsou referenční, tj. užitečné pro ty s porozuměním věci vyhledat syntaxi. Pro někoho nového v daném tématu je často kryptický a formální, aby byl užitečný. Zjistíte, že přijatá odpověď je asi 10krát tak dlouhá, jako položka manuálové stránky, a že ‚ je to z nějakého důvodu.
  • Dokonce i stará stránka POSIX 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ých man 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ěď.
  • @Philippos 🙂 Ach, to by bylo vzrušující věc vyzkoušet, na úrovni “ vzrušující “ který je mimo rozsah mého grafu.
  • Přijďte na @Philippos. Opravdu říkáš Kusalanandovi, že se na stránce nezlepšil? 🙂
  • @ZsoltSzilagy To jsem ani neřekl, ani to nemyslel. Krmil tě velmi dobře, jen si myslím, že jsi dost starý na to, abys se najedl sám. (-;

Odpověď

Tato odpověď přichází v následujících částech:

  • Základní použití -exec
  • Použití -exec v kombinaci s sh -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:

  1. jako test k dalšímu omezení hledání.
  2. 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.

  1. 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.

  2. 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ěn find). 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, nikoli execlp("sh", "-c", "cmd arg") (pro které shell by nakonec udělal ekvivalent execlp("cmd", "arg") pokud cmd 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, provedete sh a mv pro každý nalezený soubor, který bude pro velké množství souborů znatelně pomalejší.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *