Problema de expansão automática com elementos de matriz contendo um '*' (asterisco)

1

Estou tentando escrever um script find que mais tarde poderá ler uma lista de diretórios a serem excluídos de um arquivo externo. Embora eu possa realizar essa parte sozinho, é a expansão da matriz irritante que torna a tarefa difícil. Primeiro, algumas "preparações" para obter uma árvore de diretórios de amostra adequada:

$ mkdir tmp && cd tmp
$ mkdir excl1_dirx excl2_dirx excl3_dirx
$ touch excl1_dirx/dummy1.txt excl2_dirx/dummy2.txt excl3_dirx/dummy3.txt
$ mkdir excl1_diry excl2_diry excl3_diry
$ touch excl1_diry/dummy4.txt excl2_diry/dummy5.txt excl3_diry/dummy6.txt
$ touch dummy00.txt dummy01.txt

Se o script funcionar, apenas dummy00.txt e dummy01.txt poderão ser exibidos.

#!/bin/bash
excl_d=("excl*_dirx" "excl*_diry")

find_str=" . -type f ! ( "

for ((i=0 ; i<$((${#excl_d[*]})) ; i++)); do
   if [[ $i > 0 ]]; then
     find_str+=" -o "
   fi

  find_str+=" -path \"./${excl_d[i]}/*\""
done

find_str+=" )"

# this is just for debugging
echo "[debug] value of str = find $find_str"

find $find_str

Primeiro de tudo: por que (aparentemente) complicado na linha antes do " done "? Bem, bash às vezes gosta de incomodar os usuários fazendo coisas que eles não esperam. Sem essas citações, expandirá cada elemento da matriz; e. g. excl*_dirx se tornará excl1 dirx excl2 dirx excl3 dirx , o que (obviamente) quebra a linha -path ! E que, embora eu tenha usado um par de aspas duplas por elemento de matriz, o que na verdade deveria impedir que o bash fizesse seu mal de expansão!

No entanto, o melhor ainda está por vir: a segunda última linha - quando o ( ) é escapado para \( \) - funcionará bem no shell, mas não em um script autônomo. Mesmo que isso não cause erros, o resultado da última implementação estará errado.

Eu tentei todos os tipos de combinações com aspas simples e duplas no

find_str+=" -path \"./${excl_d[i]}/*\""

linha, mas eu simplesmente não consigo fazê-lo funcionar, embora pareça absolutamente perfeito quando exibido como na segunda última linha. Parece que bash trata internamente minhas cotas de escape \" de maneira diferente das não escapadas. Ah, e as citações dentro das citações funcionam perto de todos os lugares mas por algum motivo, serão filtradas quando o operador += for usado.

Não estou apenas procurando uma explicação para esse comportamento, mas também para uma solução de como isso poderia funcionar em um script autônomo. Deve ser um erro estúpido que eu cometi. : - /

    
por syntaxerror 28.09.2012 / 18:31

2 respostas

1

O problema aqui é que find_str é uma string, que é então usada como uma lista de strings. o comentário de jw013 está correto , e leia Estou tentando colocar um comando em uma variável, mas os casos complexos sempre falham! . Você não está colocando o comando inteiro em uma variável, mas o problema surge assim que você tenta encher várias palavras em uma variável de string.

Em shells Bourne / POSIX, esse é um mal necessário. Mas em ksh / bash / zsh, há uma maneira melhor: use uma matriz.

#!/bin/bash
excl_d=("excl*_dirx" "excl*_diry")
find_str=( . -type f \! \( )
for ((i=0 ; i<$((${#excl_d[*]})) ; i++)); do
   if [[ $i > 0 ]]; then
     find_str+=( -o )
   fi
  find_str+=( -path "./${excl_d[i]}/*" )
done
find_str+=( \) )
find "$find_str[@]}"

Existe uma maneira mais fácil de expressar esse filtro.

#!/bin/bash
exclude_patterns=("excl*_dirx" "excl*_diry")
exclude_args=()
while [[ ${#exclude_patterns} -gt 0 ]]; do
  exclude_args+=( -path "./${exclude_patterns[1]}/*" -prune -o )
  shift exclude_patterns
done
find "${exclude_args[@]}" -type f
    
por 29.09.2012 / 02:19
1

Aqui você está:

#!/bin/bash
excl_d=("excl*_dirx" "excl*_diry")

find_str=". "

for ((i=0 ; i<$((${#excl_d[*]})) ; i++)); do
    if [[ $i > 0 ]]; then
    find_str+=" -o "
    fi

    find_str+=" -path \"./\${excl_d[i]}\" -prune "
done

find_str+=" -o -type f -print "

# this is just for debugging
echo "[debug] value of str = find $find_str"

eval "find ${find_str}"

O problema aqui é que quando o bash executa uma linha no script find ${find_str} , então executa find com apenas um argumento, ie argc argumento da função main em find será igual 2. eval em contraste compõe a string que então passa tokenizing no bash.

    
por 28.09.2012 / 19:05