Shell esquisitices redirecionamento de entrada scripting

votos
16

Alguém pode explicar esse comportamento? Corrida:

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

resulta em nada sendo ouput, 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 tubo não deve fazer em um passo que o redirecionamento para test.file fez no segundo exemplo? Tentei o mesmo código com ambas as conchas de traço e bash e tem o mesmo comportamento de ambos.

Publicado 05/08/2008 em 20:26
fonte usuário
Em outras línguas...                            


9 respostas

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

não produz nenhuma saída porque condutas executar cada um dos seus componentes dentro de uma subcamada. Subshells herdam cópias de variáveis do shell pai, em vez de compartilhá-los. Tente 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 é executado em um subnível, e US $ foo mantém o seu valor original depois de ser modificado dentro deles.

Agora tente o seguinte:

#!/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, não subshell é criado, e os US $ foo modificado dentro das chaves é o mesmo $ foo modificado fora deles.

Agora tente o seguinte:

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

Dentro das chaves, a leitura embutido cria $ var1 e $ var2 corretamente e você pode ver que eles se repetiu. Fora as chaves, eles não existem mais. Todo o código dentro das chaves foi executado em um subshell porque é um componente de um gasoduto .

Você pode colocar valores arbitrários de código entre chaves, assim você pode usar esta construção tubulação-em-a-bloco sempre que você precisa para executar um bloco de script que analisa a saída de outra coisa.

Respondeu 19/09/2008 em 03:20
fonte usuário

votos
9

Isso já foi respondida corretamente, mas a solução ainda não foi declarado. Use ksh, não bater. 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, pois de pequenas sutilezas como este. (Bash é o shell melhor interativa, na minha opinião.)

Respondeu 15/08/2008 em 16:52
fonte usuário

votos
8

Uma recente adição bashé a lastpipeopção, que permite que o último comando em um pipeline para executar no shell atual, não um subnível, quando o controle do trabalho é desativado.

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

vai realmente de saída

hello
world
Respondeu 26/07/2012 em 13:10
fonte usuário

votos
8
read var1 var2 < <(echo "hello world")
Respondeu 17/09/2008 em 01:17
fonte usuário

votos
5

Allright, eu descobri-lo!

Este é um bug difícil de pegar, mas os resultados da forma como tubos são manipulados pelo shell. Cada elemento de um gasoduto é executado em um processo separado. Quando os conjuntos de comandos de leitura var1 e var2, é define-lhes que o seu próprio subnível, não o shell pai. Então, quando as saídas subshell, os valores de var1 e var2 são perdidas. Você pode, no entanto, tentar fazer

var1=$(echo "Hello")
echo var1

que retorna a resposta esperada. Infelizmente isso só funciona para variáveis ​​individuais, você não pode definir muitos de cada vez. A fim de definir múltiplas variáveis ​​em um momento você deve ler em uma variável e pique-o em múltiplas variáveis ​​ou usar algo como isto:

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

Enquanto eu admito que não é tão elegante como a utilização de um tubo, ele funciona. Claro que você deve ter em mente que ler era para ler a partir de arquivos em variáveis, assim tornando-se ler da entrada padrão deve ser um pouco mais difícil.

Respondeu 05/08/2008 em 21:09
fonte usuário

votos
4

Minha opinião sobre esta questão (usando Bash):

read var1 var2 <<< "hello world"
echo $var1 $var2
Respondeu 04/03/2009 em 10:52
fonte usuário

votos
4

O post foi respondida corretamente, mas eu gostaria de oferecer um um forro alternativa que talvez pudesse ser de alguma utilidade.

Para atribuir valores separados de espaço de echo (ou saída padrão para essa matéria) que desembolsar variáveis, você poderia considerar o uso de matrizes de shell:

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

Neste exemplo var é uma matriz e o conteúdo pode ser acedido usando o construto $ {var [índice]}, em que o índice representa o índice de matriz (começa com 0).

Dessa forma, você pode ter tantos parâmetros como você deseja atribuir o índice da matriz relevante.

Respondeu 14/09/2008 em 18:00
fonte usuário

votos
4

É porque a versão tubo é a criação de um subnível, que lê a variável em seu espaço local, que, em seguida, é destruído quando as saídas subshell.

Executar este comando

$ echo $$;cat | read a
10637

e usar pstree -p olhar para os processos em execução, você verá uma shell adicional pendurado de seu escudo principal.

    |                       |-bash(10637)-+-bash(10786)
    |                       |             `-cat(10785)
Respondeu 05/08/2008 em 21:00
fonte usuário

votos
3

Experimentar:

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 subshell que é destruída quando essa saídas subshell. Os evita acima destruindo a subcamada até que o resultado tenha sido echo'd. Outra solução é:

result=`echo "hello world"`
read var1 var2 <<EOF
$result
EOF
echo $var1
echo $var2
Respondeu 17/09/2008 em 03:15
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more