Armazenar aspas na variável para usar como parâmetro de linha de comando Bash

6

Estou escrevendo um script para chamar um comando (weka, se você estiver curioso) algumas vezes com alguns parâmetros diferentes. Um dos parâmetros, '-search "< stuff >" ' precisa ser citado. Em alguns casos, eu preciso usar vários desses parâmetros, por exemplo.

weka a -search "a params" -search "other a params"

weka b -search "just these params"'

Estou tentando usar um array associativo que se parece com algo como:

search=( ["a"]='-search "a params" -search "other a params"'
["b"]='-search "just these params"'

Em seguida, faça a ligação como:

function call_thing_with_the_right_parameters_so_it_works_out_alright {
    weka $1 ${search["$1"]}
} # <-- closing brace for the function
call_thing_with_the_right_parameters_so_it_works_out_alright

Ai, não importa o que eu tente, a citação fica toda bagunçada:

bash -x ./this_is_the_name_of_my_script_sorry_its_so_long
...
+ weka a -search '"a' 'params"' # <-- (not what I want)
...

Alguma idéia?

    
por Mike 28.04.2016 / 03:59

2 respostas

2

A linha no seu código weka $1 ${search["$1"]} está sendo sujeita a divisão de shell.
Se você não alterou a variável $IFS essa divisão irá acontecer na espaço guia nova linha .

A linha é expandida para:

weka $1 ${search["$1"]}
weka a -search "a params" -search "other a params"

Mas, ao ser dividido, conforme descrito acima, é isso que significa:

<weka> <a> <-search> <"a> <params"> <-search> <"other> <a> <params">

Você pode ver exatamente o mesmo printf anterior na linha:

$ printf '<%s> ' weka $1 ${search["$1"]}; echo
<weka> <a> <-search> <"a> <params"> <-search> <"other> <a> <params">

Isso ficará melhor se as variáveis forem citadas corretamente:

$ printf '<%s> ' weka "$1" "${search["$1"]}"; echo
<weka> <a> <-search "a params" -search "other a params">

Mas isso não é o que você quer. Você precisa dividi-lo, mas não em espaços simples.

O que fazer?

Existem duas soluções:

Dividir manualmente

Use algum caractere como # para marcar manualmente a posição de divisão:

search=( ["a"]='-search#a params#-search#other a params' ["b"]='-search#just these params' )

Em seguida, diga ao bash qual é o caractere usado para dividir com IFS , para dividir a string em uma nova matriz b :

IFS='#'
b=( ${search["a"]} )
printf '<%s> ' "${b[@]}"; echo

Que produz:

<-search> <a params> <-search> <other a params> 

A divisão exata que você deseja.
O único problema é que IFS foi alterado, mas podemos resolver isso em uma função fazendo IFS local:

callsplit(){
    local IFS='#'
    b=( ${search["$1"]} )
    weka "$1" "${b[@]}"
}

Use Eval

A outra solução é usar o eval para analisar novamente a linha de comando, para que o shell possa dividi-lo, assim como o modo comum de os shells dividirem as linhas.
O valor da pesquisa variável será como você definiu:

search=( ["a"]='-search "a params" -search "other a params"'  
         ["b"]='-search "just these params"' )

Mas vamos expandir a linha de execução com eval:

eval weka "$1" "${search["$1"]}"

Se você quiser ver como a linha é expandida, use:

$ eval printf "'<%s> '" weka "$1" "${search["$1"]}"; echo
<weka> <a> <-search> <a params> <-search> <other a params>

O script inteiro será:

#!/bin/bash
declare -A search
search+=( ["a"]='-search "a params" -search "other a params"')
search+=( ["b"]='-search "just these params"' )

call_thing() {
    eval weka "$1" "${search["$1"]}"
} # <-- closing brace for the function

call_thing "a"

Nota: Isso funcionará corretamente supondo que os valores da pesquisa estejam dentro do script (nenhum invasor externo poderia defini-los) e esses valores são citados como "linha de comando" de um shell comum .

Aviso : O uso de eval pode permitir que dados como uma string sejam convertidos em código como um comando. Neste script específico, esta linha:

call_thing "a; touch new_file"

Será executado o comando touch new_file . Mas qualquer outro comando também pode ser executado. Tenha cuidado, muito cuidado com o que você alimenta para eval .

Tendo dito o acima, lembre-se de que existem muitos comandos perigosos que podem ser executados no shell, como rm -r / . O comando eval não é mais poderoso que qualquer um deles. Apenas tenha cuidado.

    
por 28.04.2016 / 05:29
1

O shell não suporta cotações da maneira que você deseja. (Uma vez que as aspas são citadas, elas são tratadas como caracteres regulares.)

Você pode enganar e fazer o que quiser. Para começar, defina um array associativo:

declare -A s
s=( ["a"]='-search;a params;-search;other a params;' ["b"]='-search;just these params' )

Como você pode ver, a string tem os argumentos para cada comando separados por um ponto-e-vírgula. Para extrair, por exemplo, os argumentos a serem usados para a , informamos ao shell para usar ; como o separador de campo:

IFS=\; v=(${s[a]})

Você pode verificar se a matriz v agora tem os campos desejados da seguinte forma:

$ declare -p v
declare -a v='([0]="-search" [1]="a params" [2]="-search" [3]="other a params")'

Você pode executar weka como:

weka a "${v[@]}"

Então, colocando tudo junto, sua definição de função pode parecer:

declare -A s
s=( ["a"]='-search;a params;-search;other a params;' ["b"]='-search;just these params' )
callthing() (
        IFS=\; v=(${s[$1]})
        weka "$1" "${v[@]}"
)
    
por 28.04.2016 / 04:25

Tags