Comportamento diferente de eval “command \” $ @ \ “”, “$ @” e “$ (echo $ @)” quando usados como argumentos [duplicados]

0

Eu gostaria de passar vários argumentos para o navegador Tor (firefox) programaticamente através de uma função arbitrariamente intitulada tor . Isto é para comandar tor search terms e voila! Meus termos de pesquisa. Ao usar essas três variações,

~/tor-browser_en-US/Browser/start-tor-browser -search "$(echo $@)"

eval "~/tor-browser_en-US/Browser/start-tor-browser -search \"$@\""

~/tor-browser_en-US/Browser/start-tor-browser -search "$@"

apenas as duas primeiras linhas parecem ter um desempenho idêntico. Eles resultam em uma única janela do navegador Tor procurando por termos de conteúdo textual arbitrário. No entanto, a terceira linha resulta em uma nova janela de pesquisa por palavra.

Por que esse comportamento?

Deixe-me esclarecer. Eu não estou procurando por soluções alternativas, mas estou apenas interessado em uma explicação sobre o bahvour.

    
por Richard LeClair 28.08.2015 / 07:13

2 respostas

3

Para a terceira versão, você deseja "$*" não "$@" .

Explicação

Para ilustrar, vamos definir alguns argumentos posicionais:

$ set -- arg1 arg2 arg3

Agora, vamos lê-los com sua formulação echo :

$ printf "%s\n" "$(echo $@)"
arg1 arg2 arg3

Vamos ver o que o "$@" faz com eles:

$ printf "%s\n" "$@"
arg1
arg2
arg3

A diferença é que "$@" se expande para três palavras, enquanto "$(echo $@)" se expande para uma única string. Se você quiser um comportamento como "$(echo $@)" , use "$*" :

$ printf "%s\n" "$*"
arg1 arg2 arg3

Documentação

De man bash :

"$*" produz uma única string:

"$*" is equivalent to "$1c$2c...", where c is the first character of the value of the IFS variable.

Por contraste, man bash afirma que $@ produz uma série de palavras:

"$@" is equivalent to "$1" "$2" ...

Usando o eval

Observe:

$ echo 'printf "%s\n" "' "$@" '"'
printf "%s\n" " arg1 arg2 arg3 "

Quando o bash avalia o comando echo , "$@" expande para separar palavras. echo , no entanto, pega essas palavras separadas e as combina em uma string. Nós vimos isso antes quando olhamos para "$(echo $@)" . eval funciona de maneira semelhante. Faz uma string fora de todos os seus argumentos e avalia essa string. Assim:

$ eval 'printf "%s\n" "' "$@" '"'
 arg1 arg2 arg3 

Isso também é documentado por man bash :

eval [arg ...]
The args are read and concatenated together into a single command. This command is then read and executed by the shell, ...

    
por 28.08.2015 / 07:19
2

Considere um exemplo mais simples para ver a diferença:

$ set "a b" c "d e"
$ printf "%s\n" "$@"
a b
c
d e

O que precede é o que você deve usar; é simples, fácil de entender e correto.

$ printf "%s\n" "$(echo $@)"
a b c d e

Aqui, $@ primeiro expande sem aspas (as aspas em torno da substituição de comandos são separadas e ainda não aplicadas), então é equivalente a $(echo a b c d e) e printf tem um argumento adicional após a string de formatação.

$ eval "printf \"%s\n\" \"$@\""
a b c d e

Aqui, $@ é citado, mas a expansão produz 3 argumentos separados para eval ( printf "%s\n" a b , c e d e ), que são concatenados em um único comando que produz a mesma saída que número 2 acima.

    
por 28.08.2015 / 07:25