no bash, lido depois que um pipe não está definindo valores

14

Editar: o título original foi "read fails in bash"

Com o ksh, estou usando a leitura como uma maneira conveniente de separar valores:

$ echo 1 2 3 4 5 | read a b dump
$ echo $b $a 
2 1
$

Mas falha no bash:

$ echo 1 2 3 4 5 | read a b dump
$ echo $b $a 

$

Eu não encontrei um motivo na página man porque isso falha, alguma ideia?

    
por Emmanuel 11.07.2014 / 12:34

2 respostas

18

bash executa o lado direito de um pipeline em um < href="https://www.gnu.org/software/bash/manual/bashref.html#Command-Execution-Environment"> contexto subshell , portanto, alterações em variáveis (que é o que read faz ) não são preservados - eles morrem quando o subshell, no final do comando.

Em vez disso, você pode usar a substituição de processos :

$ read a b dump < <(echo 1 2 3 4 5)
$ echo $b $a
2 1

Nesse caso, read está sendo executado em nosso shell principal e nosso comando de produção de saída é executado no subshell. A sintaxe <(...) cria uma subshell e conecta sua saída a um canal, que redirecionamos para a entrada de read com a < operation . Como read foi executado em nosso shell principal, as variáveis estão definidas corretamente.

Como apontado em um comentário, se seu objetivo é literalmente dividir uma string em variáveis de alguma forma, você pode usar um aqui string :

read a b dump <<<"1 2 3 4 5"

Eu suponho que há mais do que isso, mas esta é uma opção melhor se não houver.

    
por 11.07.2014 / 12:40
12

Este não é um erro bash , pois POSIX permite os comportamentos bash e ksh , levando à infeliz discrepância que você está observando.

link

Além disso, cada comando de um pipeline multi-comando está em um ambiente subshell; como uma extensão, no entanto, qualquer um ou todos os comandos em um pipeline podem ser executados no ambiente atual. Todos os outros comandos devem ser executados no ambiente atual do shell.

No entanto, com bash 4.2 e mais recente, você pode definir a opção lastpipe em scripts não interativos para obter o resultado esperado, por exemplo:

#!/bin/bash

echo 1 2 3 4 5 | read a b dump
echo before: $b $a 
shopt -s lastpipe
echo 1 2 3 4 5 | read a b dump
echo after: $b $a 

Saída:

before:
after: 2 1
    
por 12.07.2014 / 11:18