Como dito muitas vezes neste site, deixando uma expansão variável (como em $var ) ou substituição de comando (como em 'cmd' ou $(cmd) ) (ou expansão aritmética (como em $((11 * 11)) ) na maioria dos shells ) não citada em shells Bourne / POSIX é o operador split + glob.
O conteúdo de $var ou a saída de cmd (sem os caracteres de nova linha à direita) é dividido de acordo com o valor atual da variável $IFS special (que por padrão contém os caracteres SPC, TAB e NL (e NUL em zsh )) e cada palavra resultante dessa divisão está sujeita à geração de nome de arquivo, também conhecida como globbing .
Por exemplo, se find ouputs ./foo bar.pdf\n./*foo*\tbar.pdf\n ( \t significando TAB e \n NL), com o valor padrão $IFS , a substituição de comando será expandida para ./foo bar.pdf\n./*foo*\tbar.pdf (nova linha final removida) e, em seguida, ser dividido em ./foo , bar.pdf , ./*foo* e foo.pdf e ./*foo* , que é um padrão curinga, será expandido em tantos argumentos quanto houver arquivos não ocultos no diretório atual cuja nome contém foo .
Se você quiser dividir somente os caracteres de nova linha, precisará definir $IFS somente para nova linha:
IFS='
'
Se você não quiser que os padrões de caracteres curinga sejam expandidos, será necessário desativá-lo com
set -f
No entanto, observe que a nova linha é um caractere tão válido quanto qualquer outro em um nome de arquivo, portanto, de maneira mais geral, a saída find -print não pode ser processada de forma confiável.
Uma saída como:
./a.pdf
./b.pdf
quer dizer os arquivos a.pdf e b.pdf no diretório atual, ou o arquivo chamado b.pdf no diretório a.pdf\n. .
Algumas implementações de find como o GNU find (de onde é originado) têm um predicado -print0 para gerar o nome do arquivo seguido por um caractere NUL em vez de um caractere NL. Com o padrão find , você pode usar -exec printf '%s com o mesmo resultado. NUL é o único caractere que não pode ocorrer em um nome de arquivo. zsh' {} +
No entanto, $IFS é o único shell que pode armazenar um caractere NUL em suas variáveis (como o caractere set -f ), portanto:
IFS=$'find . -name '*.pdf' -exec the command {} \;
'
for i in 'find ... -print0'; do
...
done
(não há necessidade de zsh em zsh , pois zsh não faz globbing na substituição de comando) funcionará em find , mas não em outras shells.
Melhor e, portably, é ter zsh chamando os comandos que você deseja executar nesses arquivos. Como @Gnouc sugere:
find . -name '*.pdf' -exec sh -c '
for i do
something complex with "$i"
done' sh {} +
Se você precisar de algo mais complexo envolvendo declarações de shell, ainda é possível fazer coisas como:
find . -name '*.pdf' -print0 |
while IFS= read -r -d '' file; do
whatever with "$file"
done
Com bash ou zsh , você também pode fazer:
find . -name '*.pdf' -type f -exec ls -ld {} +
No entanto, observe que o stdin dentro do loop é afetado.
find (desde 1990) tem a maior parte da funcionalidade (*/)# incluída em seus recursos de globalização por meio de uma sintaxe na qual você pode especificar qualquer nível de subdiretórios ( **/ sintaxe ou sua forma mais simples -type f ) e globbing qualificadores (que são o pingente de -mtime , -perm , find ... em **/ ).
A parte ksh93 disso foi copiada por fish em 2003, bash em 2005, tcsh em 2009 e tcsh em 2010 (embora ***/ também copiasse a parte bash ). E todos eles não permitem isso por padrão. Infelizmente, observe que fish e ** -L seguem os links simbólicos para os diretórios (como -follow / find em *** ou zsh em tcsh ou pdf ).
Nesses shells, você pode encontrar find arquivos em qualquer nível de subdiretórios sem precisar depender de fish , mas observe que a advertência está acima de bash e zsh , e apenas zsh tem o globbing qualificadores.
Assim, por exemplo, o equivalente bash de:
ls -ld ./**/*.pdf(D.)
seria:
shopt -s failglob
shopt -s globstart
files=(./**/*.pdf) &&
for i do
[ -f "$i" ] && ! [ -L "$i" ] && set -- "$i" "$@"
shift
done && ls -ld "$@"
Enquanto com %code% , você precisa fazer algo como:
IFS='
'