A resposta da BLay está correta, mas para desconstruir o que está realmente acontecendo aqui (ignorando o erro de digitação do -name
primária):
#!/bin/bash
while IFS= read -r -d '' file; do
files+=$file
done < <(find -type f -name '*.c' -print0)
echo "${files[@]}"
No shell iniciado pela substituição do processo ( <(...)
), o seguinte comando é analisado por bash:
find -type f -name '*.c' -print0
Como o glob *.c
é citado, o bash não o o expande. No entanto, as aspas simples são removidas. Portanto, quando o processo find
for iniciado, o que ele vê como sua lista de argumentos é:
-type
f
-name
*.c
-print0
Observe que esses argumentos são separados por bytes nulos , não por espaços ou novas linhas. Isso está no nível C, não no nível do shell. Isso tem a ver com a forma como os programas são executados usando execve()
em C.
Agora, para contrastar, no seguinte snippet:
#!/bin/bash
find_args="-type f -name '*.c' -print0"
while IFS= read -r -d '' file; do
files+=$file
done < <(find $find_args)
echo "${files[@]}"
O valor da variável find_args
está definido como:
-type f -name '*.c' -print0
(As aspas duplas não fazem parte do valor, mas os caracteres de aspas simples são . )
Quando o comando find $find_args
é executado, conforme man bash
, o token $find_args
está sujeito à expansão de parâmetro seguido por divisão de palavras seguida por expansão do nome do caminho (também conhecido como expansão glob).
Após a expansão do parâmetro, você tem -type f -name '*.c' -print0
. Note que isso é após remoção de cotação. Assim, as aspas simples não serão removidas.
Após a divisão de palavras, você tem as seguintes palavras separadas:
-type
f
-name
'*.c'
-print0
Então vem a expansão do nome do caminho. É claro que '*.c'
provavelmente não corresponderá a nada, já que você normalmente não coloca aspas nos seus nomes de arquivo, então o resultado provável será que '*.c'
será passado como um padrão literal para find
e, portanto, o -name
primary falhará em todos os arquivos. (Teria sucesso apenas se houver um arquivo cujo nome comece com uma aspa simples e termine com os três caracteres .c'
)
Editar: Na verdade, se houver um arquivo desse tipo, o glob '*.c'
será expandido para corresponder a esse arquivo e qualquer outro desses arquivos e então a expansão [o nome do arquivo real] será passada para find
como um padrão . Então, se o -print0
primário será alcançado ou não, depende de (a) se existe apenas um nome de arquivo, e ( b) se esse nome de arquivo, interpretado como glob, corresponde a si mesmo.
Exemplos:
Se você executar touch "'something.c'"
, o glob '*.c'
expandirá para 'something.c'
e, em seguida, o find
primary -name 'something.c'
corresponderá a esse arquivo também e será impresso.
Se você executar touch "'namewithcharset[a].c'"
, o glob '*.c'
será expandido para esse pelo shell, mas o find
primary -name 'namewithcharset[a].c'
não será não correspondente - corresponderia apenas a 'namewithcharseta.c'
, que não existe, por isso -print0
não seria alcançado.
Se você executar touch "'x.c'" "'y.c'"
, o glob '*.c'
será expandido para ambos nomes de arquivos, o que causará um erro na saída de find
porque 'y.c'
não é um primário válido (e não pode ser como não começa com um hífen).
Se a opção nullglob
estiver definida, você terá um comportamento diferente.
Veja também: