Script shell, find -name e expansão de curinga

3

Estou tentando usar find -name em um script sh com um argumento complexo computado anteriormente para a condição. Simplificado, é como

cond="-name '*.txt*"
find . $cond -ls

Mas agora tenho o problema de que o curinga em $cond é expandido pelo shell antes de chamar find ou não expandido por find .

Para testar isso, em um diretório de teste vazio que fiz:

touch a.txt b.txt c.dat

e depois iterado

cond="-name '*.txt'"; echo $cond; find . $cond

com valores diferentes para $cond , usando todos os tipos de citação / escape que eu consegui pensar.

Ou eu recebo todas as quatro entradas (incluindo o diretório . ) ou nenhum arquivo - ou find reclama que b.txt é um primário ou operador desconhecido. Naturalmente, a saída correta deve ser apenas os dois arquivos de texto.

Alguma dica? Tenho certeza de que estou sentindo falta de algo básico.

    
por Stefan 03.11.2014 / 12:17

1 resposta

5

Em shells do tipo Bourne (exceto zsh ), deixar uma expansão de variável sem aspas no contexto da lista é o operador split + glob . Em:

cond="-name '*.txt'"; echo $cond

O conteúdo de $cond é dividido primeiro de acordo com o valor da variável especial $IFS . Por padrão, isso é no espaço ASCII, na guia e nos caracteres de nova linha.

Então, isso é divisão em -name e '*.txt' . Então a parte glob é aplicada. Para cada uma dessas palavras que contém caracteres curinga (aqui * no segundo), a palavra é expandida para a lista de arquivos que correspondem a esse padrão ou deixada intacta se nenhum arquivo for correspondido. Portanto, '*.txt' se expandiria para a lista de arquivos no diretório atual cujo nome começa com ' e termina em .txt' ou para '*.txt' se nenhum arquivo corresponder.

Essa lista de palavras é então passada como argumentos separados para echo . Se nenhum arquivo corresponder, isso significa que echo receberá 3 argumentos: "echo" , "-name" e "'*.txt'" .

Naturalmente, o mesmo se aplica ao comando find .

Você deseja que o comando find receba os argumentos -name e *.txt , portanto é necessário ajustar seu operador split + glob .

Você precisa do valor de $IFS para corresponder ao separador que você usa em $cond . Então, por exemplo:

cond='-name:*.txt'
IFS=':'

E você não quer a parte glob do operador split + glob , então você precisa desativá-lo com:

set -f

Então se torna:

cond='-name:*.txt'
IFS=:; set -f
find . $cond -ls

Ou você poderia fazer:

cond='-name *.txt'
set -f
find . $cond -ls

e assuma que $IFS não tenha sido modificado de seu valor padrão.

Alternativamente, se o seu shell suporta arrays, você pode querer usar isso em vez de confiar em variáveis escalares e esperar que o shell o divida na expansão.

Isso funcionaria com ksh93 , mksh , bash ou zsh :

cond=(-name '*.txt') # an array with two elements: "-name" and "*.txt"
find . "${cond[@]}" -ls # elements of the array expanded as separate arguments
                        # to find.
    
por 03.11.2014 / 12:32