Tato otázka je inspirována
Proč je používání shellové smyčky ke zpracování textu považováno za špatný postup?
Vidím tyto konstrukce
for file in `find . -type f -name ...`; do smth with ${file}; done
a
for dir in $(find . -type d -name ...); do smth with ${dir}; done
jsou zde používány téměř denně, i když si někteří lidé najdou čas na komentování těchto příspěvků a vysvětlují, proč je třeba se takovým věcem vyhnout...
Když vidím počet takových příspěvků (a skutečnost, že někdy jsou tyto komentáře prostě ignored) Myslel jsem, že bych mohl položit otázku:
Proč je smyčka přes find
špatný postup výstupu a jaký je správný způsob spuštění jednoho nebo více příkazů pro každý název/cestu souboru vrácenou find
?
Přijatá odpověď:
Problém
for f in $(find .)
spojuje dvě neslučitelné věci.
find
vytiskne seznam cest k souborům oddělených znaky nového řádku. Zatímco operátor split+glob, který se vyvolá, když opustíte $(find .)
unquoted v kontextu tohoto seznamu jej rozdělí na znaky $IFS
(ve výchozím nastavení zahrnuje nový řádek, ale také mezeru a tabulátor (a NUL v zsh
)) a u každého výsledného slova provede globbing (kromě zsh
). ) (a dokonce i expanze složené závorky v derivátech ksh93 nebo pdksh!).
I když to zvládnete:
IFS='
' # split on newline only
set -o noglob # disable glob (also disables brace expansion in pdksh
# but not ksh93)
for f in $(find .) # invoke split+glob
To je stále špatně, protože znak nového řádku je platný jako kterýkoli znak v cestě k souboru. Výstup příkazu find -print
jednoduše nelze spolehlivě dodatečně zpracovat (kromě použití nějakého spletitého triku, jak je uvedeno zde ).
To také znamená, že shell potřebuje uložit výstup find
úplně a pak to rozdělit+globovat (což znamená uložit tento výstup podruhé do paměti), než začnete procházet soubory.
Všimněte si, že find . | xargs cmd
má podobné problémy (mezery, nový řádek, jednoduché uvozovky, dvojité uvozovky a zpětné lomítko (a s některými xarg
implementační bajty netvořící součást platných znaků) jsou problémem)
Správnější alternativy
Jediný způsob, jak použít for
smyčka na výstupu find
by bylo použít zsh
který podporuje IFS=$'