/ bin / sh: variável de leitura do canal [duplicado]

1

Em bash ou zsh , posso usar a seguinte sintaxe para ler do pipe em variáveis:

echo AAA BBB | read X Y ; echo $X

, que imprimirá AAA

Por que o mesmo não funciona em /bin/sh ?

Estou usando /bin/sh - > /bin/dash no Debian

    
por Martin Vegter 11.09.2018 / 08:41

4 respostas

3

Why does the same not work in '/bin/sh' ?

Atribuir variáveis em um pipe não funciona como esperado em sh e bash porque cada comando de um pipe é executado em um subshell. Na verdade, o comando funciona , X e Y get declarados, mas eles não estão disponíveis fora do canal.

Os seguintes itens funcionarão:

echo AAA BBB | { read X Y ; echo $X; }

Mas no seu caso:

tente isso,

read X Y <<< "AAA BBB"

ou

read X Y < <(echo "AAA BBB")

Alguns links úteis:

por 11.09.2018 / 08:48
2

in bash or zsh, I can use following syntax to read from pipe into variables.

echo AAA BBB | read X Y ; echo $X

Não, você não pode. Não no Bash com as configurações padrão.

$ ./bash5.0-alpha -c 'echo AAA BBB | read X Y ; echo "Bash=$BASH_VERSION X=\"$X\""'
Bash=5.0.0(1)-alpha X=""
$ bash -c 'echo AAA BBB | read X Y ; echo "Bash=$BASH_VERSION X=\"$X\""'
Bash=4.4.12(1)-release X=""

O Bash executa todos os comandos em um pipeline em ambientes subshell separados, portanto, as alterações nas variáveis do shell não são visíveis fora do pipeline. Dash é semelhante aqui.

Zsh e ksh (implementações AT & T, não pdksh ou derivadas) executam o último comando do pipeline no ambiente principal do shell, então funciona:

$ zsh -c 'echo AAA BBB | read X Y ; echo "Zsh=$ZSH_VERSION X=\"$X\""'
Zsh=5.3.1 X="AAA"

No Bash, você pode usar shopt -s lastpipe para fazer o que o ksh e o zsh fazem (só funciona em shells não-interativos):

$ bash -O lastpipe -c 'echo AAA BBB | read X Y ; echo "Bash=$BASH_VERSION X=\"$X\""'
Bash=4.4.12(1)-release X="AAA"

Mas não acho que exista essa opção para o Dash.

No Bash você também pode usar a substituição de processos ao invés do pipe, mas isso não é uma opção no Dash.

As soluções alternativas girariam em torno de tornar o lado direito do loop uma instrução composta ou uma função e, assim, usar o valor lido do canal no mesmo ambiente em que foi lido.

$ dash -c 'echo AAA BBB | { read X Y ; echo "X=\"$X\""; } '
X="AAA"
$ dash -c 'f() { read X Y ; echo "X=\"$X\""; }; echo AAA BBB | f'
X="AAA"

Ou use um documento aqui:

read X Y << EOF
$(echo AAA BBB)
EOF
    
por 11.09.2018 / 10:27
0

Não, isso não define X e Y (após o ponto-e-vírgula).

echo AAA BBB | read X Y ; echo $X
$ bash -c 'echo AAA BBB | read X Y ; echo "<$X>"'
<>

O mesmo acontece no traço.

Para fazer isso funcionar, você precisa recorrer a soluções antigas (aqui-doc):

$ read X Y <<_EOT_
> AAA BBB
> _EOT_
$ echo "<$X>"
<AAA>

Como um comando para tentar shells (infelizmente as novas linhas não podem ser removidas (não há um liner)):

$ bash -c 'read X Y <<_EOT_
AAA BBB
_EOT_
echo "<$X>" '
<AAA>

Isso funciona exatamente da mesma maneira em dash, zsh, ksh e muitos outros:

$ dash -c 'read X Y <<_EOT_
AAA BBB
_EOT_
echo "<$X>" '
<AAA>

Alternativas mais recentes (que não funcionam em traço) para o here-doc podem ser:

  1. here-string (funciona no bash, ksh, zsh):

    $ bash -c 'read X Y <<<"AAA BBB"; echo "<$X>" '
    <AAA>
    
  2. Substituição de processos (funciona no bash, ksh, zsh):

    $ bash -c 'read X Y < <(echo AAA BBB) ; echo "<$X>" '
    <AAA>
    

Uma alternativa que imprime o valor e funciona em traço, mas não mantém a variável definida em traço ou bash (mas em ksh e zsh), é:

  1. Comando do grupo:

    $ dash -c 'echo "AAA BBB" | { read X Y ; echo "<$X>"; }; echo "<$X>" '
    <AAA>
    <>
    

    Note que esta última solução pode ser configurada para manter as variáveis configuradas no bash com a opção lastpipe (não para uso interativo) ou em ksh / zsh por padrão:

    $ bash -c 'shopt -s lastpipe;echo "AAA BBB"|{ read X Y;echo "1<$X>"; };echo "2<$X>"'
    1<AAA>
    2<AAA>
    
    $ ksh -c 'echo "AAA BBB"|{ read X Y;echo "1<$X>"; };echo "2<$X>"'
    1<AAA>
    2<AAA>
    
    $ zsh -c 'echo "AAA BBB"|{ read X Y;echo "1<$X>"; }; echo "2<$X>"'
    1<AAA>
    2<AAA>
    
por 11.09.2018 / 11:43
-3

Tenha cuidado com as atribuições de variáveis de um processo em um pipeline. O padrão POSIX não requer um comportamento específico.

Cascas modernas como ksh93 e versões recentes do Bourne Shell permitem que o shell principal seja o pai de ambos os processos em seu pipeline e, no caso de o processo mais à direita ser um comando interno, esse comando é executado no shell principal .

Outra variante é usar o método acima, mas sempre executar o comando mais à direita em outro processo.

A versão antiga é como o Bourne Shell original funcionava: os garfos de shell e o processo bifurcado criam todos os outros processos do pipe e finalmente se convertem no processo mais à direita.

A última versão precisa de muito menos código que os outros, mas é mais lenta. Por causa do tamanho do código, isso foi usado em 1976.

A primeira variante é a variante mais rápida, mas precisa de mais código que as outras, mas é a única variante que executa a atribuição de variável no processo de shell original, que é necessário para ter o valor da variável modificada no shell principal.

    
por 11.09.2018 / 09:45

Tags