Problema com expansão de caractere curinga no intervalo de loop

1

Estou tendo um problema aqui em um script bash que eu fiz. Em um loop for, eu faço iteração em todos os argumentos para construir uma variável de string que é posteriormente alimentada para um comando "eval":

    for arg in "$*"
    do
        if [ $arg != $lastArg ]; then
            findTarget+="-name $arg -o "
        else
            findTarget=$(echo $findTarget | sed 's/-o$//')
            break
        fi
    done

O problema decorre do "$ *". Por exemplo, quando eu insiro "* .c" nos argumentos e a pasta atual contém arquivos que correspondem a esse padrão, o argumento *.c é expandido para esses arquivos; Eu não quero isso, eu quero findTarget para ser concatenado com -name *.c -o , eu tentei com e sem citações, usando eval, nada parece funcionar. Alguma idéia de como fazer isso (simplesmente se possível)? Nota: o número total de argumentos pode variar.

Este é um exemplo de como eu executo o script:

$ trouver.bash *.c *.f90 someString

No final do meu loop, a variável findTarget deve ler -name *.c -o -name *.f90

Isso não funciona se o *.c ou *.f90 corresponder aos arquivos da pasta atual ...

    
por Valentin B. 05.10.2016 / 10:52

2 respostas

1

A sintaxe para repetir os parâmetros posicionais é for arg do sozinho. "$*" é a concatenação dos parâmetros posicionais com o primeiro caractere de $IFS , então você estaria fazendo um loop apenas sobre um elemento.

Além disso, se você quiser criar uma lista de argumentos para o comando find , será necessário um array, não uma string. E não se esqueça de citar suas variáveis!

Então:

findTarget=()
or=()
for arg
do
    [ "$arg" = "$lastArg" ] && break
    findTarget+=("${or[@]}" -name "$arg")
    or=(-o)
done

find . \( "${findTarget[@]}" \)

Note que quando você invoca seu script, você precisa citar os padrões *.c ..., caso contrário eles seriam expandidos pelo shell antes de serem passados para o script.

trouver.bash '*.c' '*.f90' someString

Se o seu shell interativo for zsh , você poderá definir um alias para seu comando em que o globbing esteja desativado com:

alias trouver.bash='noglob trouver.bash'

Dessa forma, você pode fazer:

trouver.bash *.c *.f90 someString

sem o shell expandindo os *.c *.f90 globs.

    
por 05.10.2016 / 11:16
1

O uso de "$*" significa unir todos os argumentos em um longo. Alterá-lo para "$@" deve fornecer uma lista de argumentos. Mas não há necessidade real de usar for arg in "$@" , pois for arg é o idioma correto.

Em seguida, para adicionar um -o para cada novo argumento, podemos usar ${findTarget[@]+"-o "} .
Será nulo se nenhum valor tiver sido atribuído a findTarget e será a string simples -o se findTarget tiver qualquer valor atribuído. Na primeira execução do loop, ele entrará em colapso para nada. Nas próximas execuções, ele se tornará -o .

(( $# < 1 )) && echo "please provide some argument(s)" && exit 1

unset  findTarget
for    arg
do     [[ "$arg" == "$lastArg" ]] && break
       findTarget+=("${findTarget[@]+"-o "}-name $arg")
done

find . \( "${findTarget[@]}" \)

Você precisa se lembrar de executar o script desta forma:

$ trouver.bash '*.c' '*.f90' someString

como você deseja que cada argumento não seja expandido pelo shell (use aspas simples. As aspas duplas podem funcionar, mas não expressam a intenção de manter os argumentos sem expansão com clareza).

Se você precisar usar o script sem as "aspas simples", poderá remover a "expansão do nome do caminho" (set -f) para evitar a expansão de * .

$ ( set -f; trouver.bash *.c *.f90 someString )

O () evitará que a alteração no noglob afete o atual shell em execução.

Isso funciona porque o script grava os resultados em stdout , que também funciona imprimindo de dentro do sub-shell (…) .

    
por 06.10.2016 / 00:03