Linux bash: atribuição de múltiplas variáveis

121

Existe no linux bash algo semelhante ao seguinte código em PHP:

list($var1, $var2, $var3) = function_that_returns_a_three_element_array() ;

ou seja, você atribui em uma frase um valor correspondente a 3 variáveis ​​diferentes.

Digamos que eu tenha a função bash myBashFuntionque grava em stdout a string "qwert asdfg zxcvb". É possível fazer algo como:

(var1 var2 var3) = ( `myBashFuntion param1 param2` )

A parte à esquerda do sinal de igual não é uma sintaxe válida, é claro. Só estou tentando explicar o que estou pedindo.

O que funciona, porém, é o seguinte:

array = ( `myBashFuntion param1 param2` )
echo ${array[0]} ${array[1]} ${array[2]}

Mas uma matriz indexada não é tão descritiva quanto nomes de variáveis ​​simples.
No entanto, eu poderia apenas fazer:

var1 = ${array[0]} ; var2 = ${array[1]} ; var3 = ${array[2]}

Mas essas são mais 3 afirmações que prefiro evitar.

Estou apenas procurando uma sintaxe de atalho. É possível?

Liberta-te
fonte

Respostas:

222

Primeira coisa que me vem à mente:

read -r a b c <<<$(echo 1 2 3) ; echo "$a|$b|$c"

saída é, sem surpresa

1|2|3
Michael Krelin - hacker
fonte
4
Existe alguma maneira de fazer isso funcionar se a primeira variável contiver um espaço?
Rucent88
7
@Michael Using read -d "\n" v1 v2 <<<$(cmd)funciona perfeitamente. Obrigado!
Rucent88
1
@LeeNetherton, bom ponto, embora eu não tenha certeza se alguém precisa do status de retorno do comando echo :-) Eu acho que no momento em que escrevo, o bash de resposta que dá suporte a essa sintaxe era menos comum (como instalado por padrão), embora eu não tenho 100% de certeza.
Michael Krelin - hacker de
4
@ MichaelKrelin-hacker com certeza, o status de retorno de não echotem sentido, mas eu estava usando essa técnica para retornar vários valores de um script que me preocupava com o status de retorno. Pensei em compartilhar minhas descobertas.
Lee Netherton,
1
Por razões de segurança, você deve usar: read -r:do not allow backslashes to escape any characters
Tom Hale
18

Eu queria atribuir os valores a uma matriz. Então, estendendo a abordagem de Michael Krelin , eu fiz:

read a[{1..3}] <<< $(echo 2 4 6); echo "${a[1]}|${a[2]}|${a[3]}"

que produz:

2|4|6 

como esperado.

raio sonoro
fonte
2
Para colocar os valores em uma matriz, já existe uma solução simples que mencionei na pergunta:a=( $(echo 2 4 6) ) ; echo ${a[0]} ${a[1]} ${a[2]}
GetFree
Sim, eu tinha esquecido isso. Eu argumentaria, entretanto, que minha sugestão é mais adequada para atribuir matrizes maiores.
soundray
@soundray Sua solução usa expansão e uma cadeia de caracteres, bash sendo o que é, duvido que funcione bem nesse cenário (mas não verifiquei).
Camilo Martin
Por razões de segurança, você deve usar: read -r:do not allow backslashes to escape any characters
Tom Hale
5

Acho que isso pode ajudar ...

Para decompor as datas inseridas pelo usuário (mm / dd / aaaa) em meus scripts, armazeno o dia, o mês e o ano em uma matriz e, em seguida, coloco os valores em variáveis ​​separadas da seguinte maneira:

DATE_ARRAY=(`echo $2 | sed -e 's/\// /g'`)
MONTH=(`echo ${DATE_ARRAY[0]}`)
DAY=(`echo ${DATE_ARRAY[1]}`)
YEAR=(`echo ${DATE_ARRAY[2]}`)
SDGuero
fonte
Por que não evitar 4 subshells mais um processo sed extra e fazer tudo isso em uma linha:IFS=/ read -r m d y < <(echo 12/29/2009)
Amit Naidu
5

Às vezes você precisa fazer algo estranho. Digamos que você queira ler um comando (o exemplo de data do SDGuero, por exemplo), mas deseja evitar múltiplos garfos.

read month day year << DATE_COMMAND
 $(date "+%m %d %Y")
DATE_COMMAND
echo $month $day $year

Você também poderia canalizar para o comando read, mas então teria que usar as variáveis ​​dentro de um subshell:

day=n/a; month=n/a; year=n/a
date "+%d %m %Y" | { read day month year ; echo $day $month $year; }
echo $day $month $year

resulta em...

13 08 2013
n/a n/a n/a
Otheus
fonte
O readcomando não acontece em um subshell por causa das chaves, é porque você tem o comando read no lado direito do tubo. Você precisa executar o readcomando no shell atual, o que você pode fazer comoread day month year <<< `date "+%d %m %Y"`
pneumatics
Não - isso read acontece, mas o escopo das variáveis ​​que ele lê sai do escopo quando o subshell do pipeline termina.
Otheus
1
Meu comentário foi sobre o motivo pelo qual a leitura acontece em uma subcamada, e percebo agora que li errado o que você escreveu. Achei que você quisesse dizer que a subcamada foi criada porque você usou as chaves ao redor da instrução composta. Mas! O motivo pelo qual você deu esse exemplo foi para evitar a bifurcação, mas o subshell bifurca também?
pneumática
O primeiro exemplo requer exatamente um fork: para que o bash possa lançar o comando date. No segundo exemplo, você configurou um pipeline entre a data e seu subconcha. Eu acho que o bash hoje em dia é inteligente o suficiente para não bifurcar no subshell, mas não tenho certeza disso. De qualquer forma, parece que sim :)
Otheus
0

O Capítulo 5 do Bash Cookbook de O'Reilly discute (com alguma extensão) as razões para a exigência em uma atribuição de variável de que não haja espaços ao redor do sinal '='

MYVAR="something"

A explicação tem a ver com a distinção entre o nome de um comando e uma variável (onde '=' pode ser um argumento válido).

Tudo isso parece um pouco como uma justificativa após o evento, mas em qualquer caso, não há menção de um método de atribuição a uma lista de variáveis.

pavium
fonte
Sim eu conheço. Acabei de adicionar espaços extras aqui e ali por uma questão de legibilidade
GetFree
Na verdade, essa é uma motivação pobre: ​​e se " ;" for um argumento válido? Quando escrevo ls ; cdainda chama lse cdapesar dos espaços. Se eu quiser listar diretórios chamados ;e cdposso apenas digitar ls ';' cd.
PieterNuyts de