Problema ao passar parâmetros contendo espaços e caracteres curingas

3

Eu tenho um problema ao passar parâmetros se os parâmetros puderem conter curingas e / ou espaços, se esses parâmetros forem opcionais. Como isso soa bem abstrato, vamos dar um pequeno exemplo: O seguinte script de shell some_command.sh espera 2 ou 3 argumentos. O primeiro argumento é suposto ser uma opção de linha de comando, o segundo argumento é opcional e, se presente, deve ser uma opção de linha de comando do formulário --NAME=VALUE , e o argumento final é obrigatório e pode ser qualquer coisa:

#!/bin/bash
# This is file some_command.sh
# Synopsis:
# some_command.sh --switch1=val1 [--switch2=val2] arg
echo "switch1: $1"
shift
if [[ "$1" == --*=* ]]
then
  echo "switch2 ($1) detected"
  shift
fi
echo argument is ${1:?argument missing}

Vamos supor que eu esteja chamando some_command.sh de algum outro script, caller.sh , da seguinte maneira:

#!/bin/bash
# This is file caller.sh
if [[ ${1:-x} == x ]]
then
  switch="--abc=long argument"
else
  switch=""
fi
some_command.sh "--exclude=*~" "$switch" arg

Observe as citações. As aspas em torno de --exclude são necessárias, porque a expressão curinga não deve ser expandida pelo shell e as cotações em torno de "$switch" são necessárias, porque $switch pode conter texto com espaços e o argumento não deve ser dividido em os espaços.

A intenção é que, se executarmos caller.sh x , isso deve resultar em

some_command.sh "--exclude=*~" "--abc=long argument"  arg

e se executarmos, digamos, caller.sh y , isso deve se transformar em

some_command.sh "--exclude=*~" arg

O caller.sh que eu forneci aqui, não funciona corretamente, porque no último caso, ele seria executado

some_command.sh "--exclude*~" "" arg

que está incorreto.

Eu tentei prefixar o comando com eval . Embora isso resolvesse o problema com $switch , também removeria as cotas em torno de "--exclude" e os curingas seriam avaliados pelo shell.

Acho que eu poderia continuar com o eval e usar um nível extra de cotação, ou seja, "\"--exclude*~\"" , mas essa é uma solução terrível. Eu me pergunto se alguém tem uma maneira mais limpa de fazer isso.

Caso você esteja se perguntando por que eu cheguei a esta pergunta: Eu tropecei no problema ao escrever scripts invocando zip , e já que esses scripts deveriam ser capazes de lidar com espaços em nomes de arquivos.

BTW, o problema, conforme indicado, ocorre com bash e zsh . Também estou interessado em soluções inteligentes, que funcionam apenas com uma dessas camadas.

    
por user1934428 17.12.2014 / 13:02

1 resposta

3

Use uma matriz, pois ela pode se expandir para um número variável de argumentos:

#!/bin/bash
# This is file caller.bash
switch=()
if [[ ${1-x} == x ]]
then
  switch=("--abc=long argument")
fi
some_command.sh "--exclude=*~" "${switch[@]}" arg

Ou você pode usar a sintaxe ${var+...} :

#!/bin/sh
# This is file caller.sh
unset switch
if [ "${1-x}" = x ]
then
  switch="--abc=long argument"
fi
some_command.sh "--exclude=*~" ${switch+"$switch"} arg

Note que com o zsh, você pode fazer:

#!/bin/zsh
switch=
if [ "${1-x}" = x ]
then
  switch="--abc=long argument"
fi
some_command.sh "--exclude=*~" $switch arg

zsh não faz split + glob na expansão do parâmetro, mas remoção vazia é o que você quer aqui.

    
por 17.12.2014 / 13:22