Bash: Atribuir saída de pipe para uma variável

2

Estou tentando obter a saída de um pipe em uma variável. Eu tentei as seguintes coisas:

echo foo | myvar=$(</dev/stdin)
echo foo | myvar=$(cat)
echo foo | myvar=$(tee)

Mas $myvar está vazio.

Eu não quero fazer:

myvar=$(echo foo)

Porque eu não quero gerar uma subcamada.

Alguma idéia?

Editar: não quero gerar um subshell porque o comando antes do pipe precisa editar as variáveis globais, o que não é possível em uma subshell. Pode? A coisa echo é apenas para simplificação. É mais como:

complex_function | myvar=$(</dev/stdin)

E eu não entendo, por que isso não funciona. Isso funciona por exemplo:

complex_function | echo $(</dev/stdin)
    
por Parckwart 17.01.2017 / 11:13

3 respostas

3

Use a substituição de comandos:

myvar='echo foo'

ou

myvar=$(echo foo)
    
por 17.01.2017 / 11:15
3

Em ksh93 , você pode usar:

var=${
  my command that updates global variable
}

Essa é uma forma de substituição de comando que não gera uma subcamada. Para comandos que são comandos internos, em vez de fazer com que eles gravem seus resultados em um canal (para o qual você precisaria de diferentes processos para ler e escrever no pipe para evitar bloqueios), ksh93 apenas faz com que eles não produzam nada, mas reunir o que eles estariam produzindo para fazer a expansão.

$ ksh -c 'a=${ b=123; echo foo;}; echo "$a $b"'
foo 123
A substituição de comandos do

fish também se comporta assim:

$ fish -c 'set a (set b 123; echo foo); echo $a $b'
foo 123

Na maioria dos outros shells, você usaria um arquivo temporário:

my command that updates global variable > file
var=$(cat file) # can be optimised to $(<file) with some shells

No Linux, e com bash ou zsh (que usam arquivos temporários para <<< ), você pode fazer:

{ my command that updates global variable > /dev/fd/3 &&
  var=$(cat<&3); } 3<<< ''
    
por 15.05.2017 / 20:03
0

A solução correta é usar a substituição de comando assim:

variable=$(complex_command)

como em

message=$(echo 'hello')

Seu pipeline:

echo 'hello' | message=$(</dev/stdin)

ou

echo 'hello' | read message

realmente funciona. O único problema é que o shell que você está usando executará a segunda parte do pipeline em um subshell. Este subshell é destruído quando o pipeline sai, então o valor de $message não é retido no shell.

Aqui você pode ver que funciona:

$ echo 'hello' | { read message; echo "$message" }
hello

... mas como o ambiente do subshell é separado (e desaparecido):

$ echo "$message"

(sem saída)

Uma solução para você seria mudar para ksh93 , que é mais inteligente sobre isso:

$ echo 'hello' | read message
$ echo "$message"
hello

Outra solução para bash seria definir a opção lastpipe shell. Isso faria com que a última parte do pipeline fosse executada no ambiente atual. No entanto, isso não funciona em shells interativos, pois lastpipe exige que o controle de trabalho não esteja ativo.

#!/bin/bash

shopt -s lastpipe
echo 'hello' | read message
echo "$message"
    
por 15.05.2017 / 19:37

Tags