Eu tenho um caso de uso em que preciso ler várias variáveis no início de cada iteração e ler uma entrada do usuário no loop.
Possíveis caminhos para a solução que não sei como explorar -
- Para atribuição, use outro filehandle em vez de stdin
Use um
for
loop em vez de... | while read ...
... Não sei como atribuir várias variáveis dentro de umfor
loopecho -e "1 2 3\n4 5 6" |\ while read a b c; do echo "$a -> $b -> $c"; echo "Enter a number:"; read d ; echo "This number is $d" ; done
Se eu entendi direito, acho que você quer basicamente fazer um loop sobre listas de valores e depois
read
outra dentro do loop.Aqui estão algumas opções, 1 e 2 são provavelmente as mais sensatas.
1. Emular arrays com strings
Ter arrays 2D seria bom, mas não é realmente possível no Bash. Se seus valores não tiverem espaço em branco, uma solução alternativa para aproximar isso é colocar cada conjunto de três números em uma string e dividir as strings dentro do loop:
Claro que você também pode usar algum outro separador, por exemplo
for x in 1:2:3 ...
eIFS=: read a b c <<< "$x"
.2. Substitua o pipe por outro redirecionamento para free stdin
Outra possibilidade é fazer a
read a b c
leitura de outro fd e direcionar a entrada para isso (isso deve funcionar em um shell padrão):E aqui você também pode usar uma substituição de processo se quiser obter os dados de um comando:
while read a b c <&3; ...done 3< <(echo $'1 2 3\n4 5 6')
(a substituição de processo é um recurso bash/ksh/zsh)3. Em vez disso, obtenha a entrada do usuário do stderr
Ou, ao contrário, usando um pipe como no seu exemplo, mas faça com que o usuário insira
read
destderr
(fd 2) em vez destdin
onde o pipe vem:Ler de
stderr
é um pouco estranho, mas na verdade geralmente funciona em uma sessão interativa. (Você também pode abrir explicitamente/dev/tty
, supondo que deseja realmente ignorar qualquer redirecionamento, é isso queless
usa para obter a entrada do usuário, mesmo quando os dados são canalizados para ele.)Embora usar
stderr
assim possa não funcionar em todos os casos, e se você estiver usando algum comando externo em vez deread
, precisará pelo menos adicionar vários redirecionamentos ao comando.Além disso, consulte Por que minha variável é local em um loop 'enquanto lido', mas não em outro loop aparentemente semelhante? para algumas questões sobre
... | while
.4. Fatie partes de uma matriz conforme necessário
Suponho que você também possa aproximar uma matriz 2D copiando fatias de uma unidimensional regular:
Você também pode atribuir
${a[0]}
etc. aa
,b
etc se quiser nomes para as variáveis, mas Zsh faria isso muito mais bem .Existe apenas um
/dev/stdin
,read
será lido em qualquer lugar onde for usado (por padrão).A solução é usar algum outro descritor de arquivo em vez de 1 (
/dev/stdin
).Do código equivalente (no bash) ao que você postou [1] (veja abaixo)
, basta adicionar
0</dev/tty
(por exemplo) para ler do tty "real":Na execução:
Outra alternativa é usar
0<&2
(o que pode parecer estranho, mas é válido).Observe que a leitura de
/dev/tty
(também0<&2
) ignorará o stdin do script, isso não lerá os valores do echo:Outras soluções
O que é necessário é redirecionar uma entrada para algum outro fd (descritor de arquivo).
Válido em ksh, bash e zsh:
Ou, com exec:
Uma solução que funciona em sh (
<<<
não funciona):Mas isso provavelmente é mais fácil de entender:
1 código mais simples
Seu código é:
Um código simplificado (em bash) é:
Que, se executado, imprime:
O que está apenas mostrando que o var d está sendo lido do mesmo
/dev/stdin
.Com
zsh
, você pode escrevê-lo em vez disso:Para arrays 2D, veja também o
ksh93
shell: