Então, eu estava procurando colocar a saída unzip -Z1
em uma matriz e encontrei esta resposta ; sua primeira opção, usando mapfile
e processando substituição COM redirecionamento de entrada, funciona muito bem.
Mas então pensei: "espere, substituição de processo", que cria um descritor de arquivo a partir do stdout, "então use o redirecionamento de entrada para colocar o conteúdo no stdin?"
Isso não é equivalente a apenas tubulação?
Aparentemente não. Não, não é. Aqui tento colocar o conteúdo de ls
dentro de uma variável, mas usando um pipe: à la ls | mapfile -t test
Nada.
Mas, se eu seguir essa resposta para um T: id est mapfile -t test < <(ls)
Voilà.
Mas por quê? diff
não posso dizer a diferença. Pelo menos entre seu conteúdo .
Não consegui encontrar nada inerentemente especial sobre < <()
, além de criar um descritor de arquivo. Usando isso como teoria, tentei um HEREDOC, que faz um descritor de arquivo também. Funcionou:
mapfile -t test2 << END && printf '%s ' "${test2[@]}"
this
is
an
array
END
Mas não se for lavado stdin
via cat
, tente:
cat << END | mapfile -t test3 && printf '%s ' "${test3[@]}"
this
is
an
array
END
Então, minhas perguntas.
Essa teoria do descritor de arquivo/descoberta do HEREDOC não deveria importar mapfile
, pois todos os seus manuais dizem que ele usa "entrada padrão", certo?
Se 'entrada padrão' de alguma forma significa que deve ser um descritor mais sofisticado; porque? Por que ele não consegue usar um fluxo de entrada simples de leitura única para gerar o array?
E, finalmente, se ambos forem respondidos/no caminho certo, por que o comando não falhou quando esperava um fd? Você verá que meu terminal 0
imprimiu um monte (e usamos &&
para os últimos exemplos), isso está $?
no meu prompt
, então não houve nenhum erro relatado pelo builtin.
Eu tentei isso no Fedora 30 e RHEL 6, então não acho que seja um bug.
Funciona; é que os resultados desaparecem imediatamente depois.
A diferença é que
foo | mapfile
é um pipeline que consiste em dois novos processos – cada elemento, seja um programa externo ou um shell integrado, é executado em um subshell. Portanto, embora o mapfile ainda faça seu trabalho, ele não tem como transferir o resultado para o processo do shell pai.Para comparação, você obteria o mesmo resultado com
(mapfile < foo.txt)
ouls | while read line; do ... done
ou mesmo(foo=bar); echo $foo
. Em todos esses casos, se você comparar $BASHPID e $$, verá valores diferentes – o primeiro mostra o PID do processo do subshell sendo diferente do PID do shell principal.Ao usar um redirecionamento, o
mapfile
comando é executado no processo principal do shell e pode alterar as variáveis do shell corretamente. Não importa muito se está sendo redirecionado de um arquivo real, ou de um<<
arquivo temporário gerado, ou de um<()
arquivo mágico gerado - neste caso, o shell lida com isso como uma etapa separada e não causa um pipeline ser construido. (O comando interno de<()
ou$()
é um subshell, é claro.)Observe que a variável pode realmente ser usada no subshell – ela simplesmente não pode ser transferida para cima. Portanto, para operações únicas, a 2ª parte do pipeline pode ser bastante elaborada: