Pergunta

Alguém pode explicar esse comportamento?Correndo:

#!/bin/sh
echo "hello world" | read var1 var2
echo $var1
echo $var2

resulta na saída de nada, enquanto:

#!/bin/sh
echo "hello world" > test.file
read var1 var2 < test.file
echo $var1
echo $var2

produz a saída esperada:

hello
world

O pipe não deveria fazer em uma única etapa o que o redirecionamento para test.file fez no segundo exemplo?Tentei o mesmo código com os shells dash e bash e obtive o mesmo comportamento de ambos.

Foi útil?

Solução

Uma adição recente ao bash é o lastpipe opção, que permite que o último comando em um pipeline seja executado no shell atual, não em um subshell, quando o controle de trabalho está desativado.

#!/bin/bash
set +m      # Deactiveate job control
shopt -s lastpipe
echo "hello world" | read var1 var2
echo $var1
echo $var2

realmente produzirá

hello
world

Outras dicas

#!/bin/sh
echo "hello world" | read var1 var2
echo $var1
echo $var2

não produz saída porque os pipelines executam cada um de seus componentes dentro de um subshell.Subcascas herdar cópias das variáveis ​​do shell pai, em vez de compartilhá-las.Experimente isto:

#!/bin/sh
foo="contents of shell variable foo"
echo $foo
(
    echo $foo
    foo="foo contents modified"
    echo $foo
)
echo $foo

Os parênteses definem uma região de código que é executada em um subshell, e $foo mantém seu valor original após ser modificado dentro deles.

Agora tente isto:

#!/bin/sh
foo="contents of shell variable foo"
echo $foo
{
    echo $foo
    foo="foo contents modified"
    echo $foo
}
echo $foo

As chaves são puramente para agrupamento, nenhum subshell é criado e o $foo modificado dentro das chaves é o mesmo $foo modificado fora delas.

Agora tente isto:

#!/bin/sh
echo "hello world" | {
    read var1 var2
    echo $var1
    echo $var2
}
echo $var1
echo $var2

Dentro das chaves, o read interno cria $var1 e $var2 corretamente e você pode ver que eles são ecoados.Fora do aparelho, eles não existem mais.Todo o código entre colchetes foi executado em um subshell porque é um componente de um pipeline.

Você pode colocar quantidades arbitrárias de código entre colchetes, para poder usar essa construção de tubulação em um bloco sempre que precisar executar um bloco de script de shell que analisa a saída de outra coisa.

Isso já foi respondido corretamente, mas a solução ainda não foi declarada.Use ksh, não bash.Comparar:

$ echo 'echo "hello world" | read var1 var2
echo $var1
echo $var2' | bash -s

Para:

$ echo 'echo "hello world" | read var1 var2
echo $var1
echo $var2' | ksh -s
hello
world

ksh é um shell de programação superior por causa de pequenas sutilezas como essa.(bash é o melhor shell interativo, na minha opinião.)

read var1 var2 < <(echo "hello world")

A postagem foi respondida corretamente, mas gostaria de oferecer uma alternativa que talvez possa ser de alguma utilidade.

Para atribuir valores separados por espaço de echo (ou stdout nesse caso) para variáveis ​​de shell, você pode considerar o uso de matrizes de shell:

$ var=( $( echo 'hello world' ) )
$ echo ${var[0]}
hello
$ echo ${var[1]}
world

Neste exemplo var é um array e o conteúdo pode ser acessado usando a construção ${var[index]}, onde index é o índice do array (começa com 0).

Dessa forma, você pode ter quantos parâmetros desejar atribuídos ao índice da matriz relevante.

Minha opinião sobre esse problema (usando Bash):

read var1 var2 <<< "hello world"
echo $var1 $var2

Tudo bem, eu descobri!

Este é um bug difícil de detectar, mas resulta da maneira como os tubos são tratados pelo shell.Cada elemento de um pipeline é executado em um processo separado.Quando o comando read define var1 e var2, ele os define em seu próprio subshell, não no shell pai.Portanto, quando o subshell é encerrado, os valores de var1 e var2 são perdidos.Você pode, no entanto, tentar fazer

var1=$(echo "Hello")
echo var1

que retorna a resposta esperada.Infelizmente, isso só funciona para variáveis ​​únicas, você não pode definir muitas de uma vez.Para definir múltiplas variáveis ​​de uma vez você deve ler uma variável e dividi-la em múltiplas variáveis ​​ou usar algo assim:

set -- $(echo "Hello World")
var1="$1" var2="$2"
echo $var1
echo $var2

Embora eu admita que não é tão elegante quanto usar um cachimbo, funciona.É claro que você deve ter em mente que read foi feito para ler arquivos em variáveis, portanto, fazê-lo ler a partir da entrada padrão deve ser um pouco mais difícil.

É porque a versão do pipe está criando um subshell, que lê a variável em seu espaço local, que é destruído quando o subshell sai.

Execute este comando

$ echo $$;cat | read a
10637

e use pstree -p para observar os processos em execução, você verá um shell extra pendurado em seu shell principal.

    |                       |-bash(10637)-+-bash(10786)
    |                       |             `-cat(10785)

Tentar:

echo "hello world" | (read var1 var2 ; echo $var1 ; echo $var2 )

O problema, como várias pessoas afirmaram, é que var1 e var2 são criados em um ambiente de subshell que é destruído quando o subshell é encerrado.O procedimento acima evita a destruição do subshell até que o resultado seja ecoado.Outra solução é:

result=`echo "hello world"`
read var1 var2 <<EOF
$result
EOF
echo $var1
echo $var2
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top