Por que usar o eval necessário para passar argumentos citados

0

Eu tenho o seguinte exemplo.

#!/bin/bash
ARGUMENTS="-executors 1 -description \"The Host\" "

# call1
# error: parameter Host" is not allowed
java -jar swarm-client.jar $ARGUMENTS

# call2    
# works fine with eval
eval java -jar swarm-client.jar $ARGUMENTS

Em $ ARGUEMENTS eu tenho um argumento citado. Eu não entendo, porque o agrupamento de argumentos por citações de escape não está funcionando em call1.
Eu não entendo, porque é necessário eval para resolver o problema de citação.

Acho que não entendo o processo / a ordem de avaliação de comandos no shell. Quem pode explicar?

    
por Skip 12.01.2018 / 13:53

2 respostas

4

Você não passa argumentos citados para um comando, você passa argumentos.

Quando você entra:

cmd arg1 arg2

O shell analisa essa linha em sua própria sintaxe, em que space é um delimitador de palavras e chama cmd1 com cmd , arg1 e arg2 como argumentos.

Nota : cmd não recebe nenhum caractere de espaço em seus argumentos, os espaços são apenas operadores na sintaxe da linguagem shell.

Como quando em C, você escreve func("foo", "bar") , em tempo de execução, func recebe dois argumentos de ponteiro, ele não vê nenhum caractere ( ou , ou " ou espaço.

Também parte da sintaxe do shell está citando. " é usado para ter palavras que contenham caracteres que fazem parte da sintaxe do shell.

Quando você faz:

cmd "arg 1" arg2

cmd recebe cmd , arg 1 e arg2 como argumentos. Não vê nenhum caractere " . Esses " são usados para evitar que o espaço seja tratado como um separador de palavras na sintaxe do shell.

Agora, quando você faz:

cmd $VAR

não é o mesmo que fazer:

cmd the content of the variable

Se fosse, você teria problemas com:

VAR='foo; reboot'
echo $VAR

por exemplo.

No shell parecido com o Bourne, o conteúdo de $VAR não é passado textualmente como um único argumento para cmd (infelizmente; ele foi corrigido em alguns outros shells como rc , es , fish e, em menor grau, zsh ). Em vez disso, está sujeito a divisão e globbing ( split+glob ) e as palavras resultantes são passadas para cmd .

A divisão é feita com base nos caracteres na variável especial $IFS , por espaço padrão, tabulação e nova linha.

Para o $ARGUMENTS , que contém -executors 1 -description "The Host" , está dividindo em -executors , 1 , -description , "The e Host" . Como nenhuma dessas palavras contém caractere curinga, a parte glob não se aplica, portanto, essas palavras são passadas para cmd .

Aqui, você pode usar o operador split+glob e usar como separador para a parte de divisão um caractere que não aparece nessas palavras:

ARGUMENTS='-executors|1|-description|The Host'
IFS='|'
cmd $ARGUMENTS

Ou melhor, para shells que os suportam (como bash ), use matrizes , onde você pode ter uma variável que contenha todos esses argumentos.

eval é avaliar o código do shell. Portanto, a outra opção é ter ARGUMENTS contendo código shell (texto na sintaxe do shell, em oposição a uma lista de argumentos), e ter passado para eval para interpretação. Mas lembre-se de citar a variável para evitar o operador split + glob:

eval "cmd $ARGUMENTS"
    
por 12.01.2018 / 14:51
4

Quando uma citação está dentro de outra string, o bash a trata como apenas outro caractere. Use matrizes em vez disso:

args=(-executors 1 -description "The Host")
java -jar swarm-client.jar "${args[@]}"

Para uma boa discussão deste problema, veja "Estou tentando colocar um comando em uma variável, mas os casos complexos sempre falhar! ".

Além disso, use nomes de minúsculas ou maiúsculas para suas variáveis. O sistema usa todas as maiúsculas para seus nomes de variáveis e você não quer sobrescrever acidentalmente um deles.

Como funciona

Quando a matriz args é definida, strings separadas entre aspas se tornam itens separados na matriz. Podemos ver isso usando declare -p arrayname para examinar o que está na matriz:

$ args=(-executors 1 -description "The Host")
$ declare -p args
declare -a args=([0]="-executors" [1]="1" [2]="-description" [3]="The Host")

Como podemos ver, a string The Host é o elemento 3 na matriz.

Quando o bash expande o formulário especial "${args[@]}" , cada elemento do array se torna uma palavra separada . Desta forma, a string The Host permanece uma palavra mesmo que contenha espaços.

    
por 12.01.2018 / 14:11