Possui múltiplos espaços contendo argumentos em uma única variável?

3

Eu estava tentando depurar meu script de shell:

content_type='-H "Content-Type: application/json"'
curl $content_type -d "{"test": "test"}" example.com

Eu aprendi que isso não faz o que eu espero. Neste script, os argumentos sendo passados para curl (devido a $content_type ) são:

  1. -H
  2. "Content-Type:
  3. application/json"

em vez de (o que eu esperava):

  1. -H
  2. Content-Type: application/json

Eu sei que posso escrever o script da seguinte forma:

content_type="Content-Type: application/json"
curl -H "$content_type" -d "{"test": "test"}" example.com

e funcionaria desta forma, mas eu estou querendo saber se é possível manter vários espaços contendo argumentos em uma única variável.

Com a segunda versão (de trabalho), se eu quiser excluir o tipo de conteúdo, preciso remover duas coisas: -H e $content_type .

No entanto, quero saber se é possível colocar a opção e seu valor em uma única entidade, para que excluir / incluir o tipo de conteúdo resulte na remoção / adição de uma única entidade.

O uso de matrizes não é portátil porque não há matrizes no POSIX.

Por que o primeiro método se comporta dessa maneira?

O motivo é divisão de palavras . Quando definimos o content_type da seguinte forma:

content_type='-H "Content-Type: application/json"'

tem o valor:

-H "Content-Type: application/json"

Quando referenciamos essa variável sem aspas (ou seja, $content_type , em vez de "$content_type" ), o valor expandido se torna um assunto de divisão de palavras. De página de divisão de palavras do manual do bash:

The shell scans the results of parameter expansion, command substitution, and arithmetic expansion that did not occur within double quotes for word splitting.

Da mesma página:

The shell treats each character of $IFS as a delimiter, and splits the results of the other expansions into words using these characters as field terminators. If IFS is unset, or its value is exactly <space><tab><newline>, the default ...

Portanto, -H "Content-Type: application/json" é dividido usando <space><tab><newline> como delimitadores. Isso nos dá:

-H
"Content-Type:
application/json"
    
por Utku 04.02.2017 / 20:04

2 respostas

0

Copiando a seção relevante da resposta de Gilles :

How do I store a command in a variable?

“Command” can mean three things: a command name (the name as an executable, with or without full path, or the name of a function, builtin or alias), a command name with arguments, or a piece of shell code. There are accordingly different ways of storing them in a variable.

If you have a command name, just store it and use the variable with double quotes as usual.

command_path="$1"
…
"$command_path" --option --message="hello world"

If you have a command with arguments, the problem is the same as with a list of file names above: this is a list of strings, not a string. You can't just stuff the arguments into a single string with spaces in between, because if you do that you can't tell the difference between spaces that are part of arguments and spaces that separate arguments. If your shell has arrays, you can use them.

cmd=(/path/to/executable --option --message="hello world" --)
cmd=("${cmd[@]}" "$file1" "$file2")
"${cmd[@]}"

What if you're using a shell without arrays? You can still use the positional parameters, if you don't mind modifying them.

set -- /path/to/executable --option --message="hello world" --
set -- "$@" "$file1" "$file2"
"$@"

What if you need to store a complex shell command, e.g. with redirections, pipes, etc.? Or if you don't want to modify the positional parameters? Then you can build a string containing the command, and use the eval builtin.

code='/path/to/executable --option --message="hello world" -- /path/to/file1 | grep "interesting stuff"'
eval "$code"

Eu sugiro que qualquer pessoa leia toda a resposta de Gilles . Ele contém muitas informações úteis.

    
por 02.11.2017 / 19:09
2

Existe uma matriz padrão como estrutura no shell padrão. São os parâmetros do posicionamento. Portanto, uma solução possível é usar set -- … e "$@" . No seu caso, você faria:

set -- -H "Content-Type: application/json"
curl "$@" -d "{"test": "test"}" example.com

Observe que isso limita você a apenas uma matriz disponível. Você não pode ter um para enrolar e outro para outro programa, por exemplo. E, claro, isso acaba com os argumentos do seu script.

    
por 04.02.2017 / 23:35