Forma correta de construir linha de argumento de comprimento variável para comando externo no bash

0

Eu preciso usar o comando "find" para encontrar vários conjuntos diferentes de arquivos na minha função Bash, dependendo da entrada do script.

Então, eu tenho uma coisa como:

DAYS=30
case $1 in
A1) ARGLINE="-name 'FOO*.xml' -or -name 'BAR*.xml' -or -name 'BTT*.txt'"
    ;;
A2) ARGLINE="-name 'PO*xml' -or -name 'PR*xml'"
    ;;
...
esac
find . -maxdepth 1 -type f -mtime +${DAYS} '${ARGLINE}'

Isso funciona.

No entanto, assim que eu quiser usar variável para o número de dias para pesquisar de volta, assim:

DAYS=30
case $1 in
A1) ARGLINE="-name 'FOO*.xml' -or -name 'BAR*.xml' -or -name 'BTT*.txt'"
    ;;
A2) ARGLINE="-name 'PO*xml' -or -name 'PR*xml'"
    ;;
...
esac
if [[ $# -gt 1 ]]; then
    DAYS=$2
fi
find . -maxdepth 1 -type f -mtime +${DAYS} '${ARGLINE}'

A função falha quando encontrar não encontra nenhum arquivo correspondente, com o seguinte erro:

No command '-name' found, did you mean: Command 'uname' from package 'coreutils' (main) -name: command not found

No entanto, ele funciona corretamente, quando o número de dias é tal que o find encontra alguns arquivos. Ele também falha quando tento canalizar a saída de execução bem-sucedida em outro comando.

Como devo construir corretamente a linha de argumento para "encontrar"?

    
por Gnudiff 16.05.2018 / 10:33

2 respostas

3

Em bash , use uma matriz:

args=( '(' -name 'FOO*.xml' -or -name 'BAR*.xml' -or -name 'BTT*.txt' ')' )

Os parênteses extras estão lá para criar o agrupamento lógico booleano correto desde que você use -or ).

Em seguida, no comando find :

find ...some arguments... "${args[@]}"

Você tem um problema adicional em usar

'$ARGLINE'

Esta é uma substituição de comando, semelhante a $( $ARGLINE ) e o shell tentará executar $ARGLINE (seu valor) como um comando. É por isso que você obtém "No command '-name' found". A substituição do comando falha, mas o find é executado, e é por isso que você acha que "funciona".

    
por 16.05.2018 / 10:37
2

A questão principal aqui é que as cotações não funcionam dentro de aspas: depois de expandir uma variável, as aspas dentro dela são apenas caracteres comuns, por exemplo:

$ foo='foo "bar doo"'
$ printf "<%s>\n" $foo
<foo>
<"bar>
<doo">

Você deve usar uma matriz para armazenar argumentos de comando assim:

ARGS=(-name 'FOO*.xml' -or -name 'BAR*.xml' -or -name 'BTT*.txt')

O shell processará as cotações nesse estágio e armazenará palavras de shell distintas como elementos de matriz distintos. Use o array assim:

find . "${ARGLINE[@]}"

Se você quiser ou precisar, você pode construir a matriz peça por peça, isso deve resultar na mesma matriz:

ARGS=(-name 'FOO*.xml')
ARGS+=(-or -name 'BAR*.xml')
ARGS+=(-or -name 'BTT*.txt')

No entanto, observe que você também usou backticks para "citar" ${ARGLINE} . Isso iniciará uma substituição de comando e executará o conteúdo de ARGLINE como um comando. É daí que vem o seu erro, o shell tenta executar um programa chamado -name .

Na verdade, no seu exemplo, você não precisa de uma matriz, pois você não tem nenhum espaço em branco dentro dos argumentos. O principal problema aqui é que a diferença entre os espaços em branco entre os argumentos e os espaços em branco dentro dos argumentos se perdem quando a linha de comando é armazenada em uma string. Mas no seu caso, isso pode funcionar, não que eu recomende:

set -f       # disable globbing
ARGLINE="-name FOO*.xml -or -name BAR*.xml -or -name BTT*.txt"
find . $ARGLINE
    
por 16.05.2018 / 10:38