O zsh não pode entrar no terminal ao canalizar stdin e stdout com o comando variável que possui saída tty

11

Informação do sistema:

macOS Sierra 10.12.6
zsh 5.4.2 (x86_64-apple-darwin16.7.0)
GNU bash, version 4.4.12(1)-release (x86_64-apple-darwin16.3.0)

Role para os EXEMPLOS na parte inferior, se você quiser apenas explorar os exemplos simplificados que fiz.

NOTA: Eu não sou um grande zshusuário.


Eu estava olhando as fzfcombinações de teclas para bashe zsh.

Observe como os dois executam um comando variável $(__fzfcmd). __fzfcmdpor padrão, gera fzfpara stdout e a substituição de parâmetro apenas executa command ( fzf) resultante da saída.

Uma diferença entre o script bashe zshé que bashaquele canaliza ainda mais a saída, $(__fzfcmd)mas zshapenas a captura dentro de uma matriz. Meu palpite é que, devido a um problema, zshquando você canaliza ainda mais a saída de fzfonde não pode entrar fzfe o processo canalizado por fzfnão recebe nenhum stdin. Sua única opção é ^Zou ^C. ^Cparece embasar o processo por algum motivo. Ou talvez eles apenas o quisessem em uma matriz para que pudessem executá zle vi-fetch-history-lo . A bashversão faz alguma mágica na ligação de chave com"\e^": history-expand-line

Agora fzfnão é importante. Parece que você só precisa de um programa que produza ttypara ser chamado por substituição de parâmetro para causar esse problema. Então, mostrarei alguns exemplos mais simples.

Aqui estão alguns outros comandos ttyque podem causar esse problema em zsh:

  • vipe (execute o editor no meio de um cano)
  • 'vim -' (faça com que o vim seja lido do stdin. semelhante ao vipe, mas não será exibido como stdout)

Nos exemplos abaixo, substitua cada ocorrência de vipepor vim -se você não quiser fazer uma instalação separada. Lembre-se de que vim -não produzirá o conteúdo do editor para o stdout, como vipefaz.

EXEMPLOS:

1) echo 1 | vipe | cat            # works in both bash and zsh
2) echo 1 | $(echo vipe) | cat    # works in bash only. zsh problem with no output until I hit `^C`:
   ^C
   zsh: done                    echo 1 | 
   zsh: suspended (tty output)  $(echo vipe) | 
   zsh: interrupt               cat
   # seems like the process is backgrounded. I can still see it in jobs command

3) cat <(echo 1 | $(echo vipe))   # zsh and bash has the problem. I'm guessing because
                                  # the file isn't finished writing and cat is
                                  # blocking vipe's tty output
                                  # both their `^C` output is just:
   ^C # nothing special, as expected

4) cat < <(echo 1 | $(echo vipe)) # works in both bash and zsh
5) echo 1 | $(echo vipe) > >(cat) # works in both bash and zsh

# The following don't have and input pipe to vipe.
# Type something then send EOF with ^D
6) vipe | cat                     # works for both
7) $(echo vipe) | cat             # works for both

Agora, estou me perguntando principalmente por que 2)há um problema para, zshmas não para, bashe por que 4)e 5)corrige o problema zsh.

Os requisitos para zshter esse problema parecem ser exatamente o que eu coloquei no título:

  • tubo de entrada
  • comando executado pela substituição de variável / parâmetro que possui ttysaída
  • tubo de saída

ATUALIZAR

Eu adicionei outra solução alternativa que não causa zshesse problema 5),. É semelhante, 4)mas, em vez de redirecionar stdoutdiretamente para stin, eu o redireciono para um arquivo que redireciona para o stdinuso da substituição de processo.

dosentmatter
fonte
1
Como a saída do psdirá, em nenhum desses casos as conchas estão congeladas ou presas. Eles estão simplesmente aguardando processos filhos da maneira normal; e, de fato, voltarão a solicitar a entrada da maneira normal assim que os processos filhos forem suspensos ou encerrados. O título e o corpo da sua pergunta incluem uma premissa implícita e falsa. "Por que minha concha congela?" é uma pergunta carregada sem resposta quando seu shell não está realmente congelando em primeiro lugar. Você teria uma pergunta melhor para remover essa premissa falsa implícita.
JdeBP
Ok, eu posso mudar isso. Não é realmente congelado no sentido de que o processo não é mais capaz de executar instruções na CPU. Você está certo de que está apenas esperando. Mas não está "preso"? Está aguardando entrada, não posso fornecê-lo. Qual é o termo melhor para descrever isso de forma concisa? Não corresponde a esta descrição de hang when either a computer program or system ceases to respond to inputs
dosentmatter
1
O shell não está aguardando entrada. Está esperando por seus filhos. É melhor colocar esta questão simplesmente descrevendo o que acontece . Não crie hipóteses e inferências como "minha concha está congelada" e pergunte sobre as inferências. Descreva o que acontece e pergunte sobre isso: As seqüências de entrada do terminal de caracteres especiais (que normalmente suspendem o trabalho em primeiro plano, interrompem ou encerram o trabalho ou enviam uma indicação de EOF para a leitura do processo no terminal) não têm efeito. O que está acontecendo? Por quê? . Esta é replicável em Debian Linux e FreeBSD / TrueOS, por sinal,
JdeBP
1
Eu relatei o erro na lista de discussão do desenvolvimento zsh . Por enquanto, você deve ser capaz de contorná-lo em um subshell(echo | $(echo vipe) | cat)
Stéphane Chazelas
1
O fato de que as substituições de processos são iniciados no fundo está documentada eu acho que (ou pelo menos conhecido)
Stéphane Chazelas

Respostas:

0

Acredito que seu problema se resume a citar indevidamente suas expansões.

Citando zsh: 14 Expansão

Um comando entre parênteses precedido por um sinal de dólar, como $(...)ou citado com sotaques graves, como ' ...', é substituído por sua saída padrão, com as novas linhas finais excluídas. Se a substituição não estiver entre aspas duplas, a saída será dividida em palavras usando o parâmetro IFS. A substituição $(cat foo)pode ser substituída pelo equivalente, mas mais rápido $(<foo). Nos dois casos, se a opção GLOB_SUBST estiver configurada, a saída estará qualificada para a geração do nome do arquivo.

Observe que o Exemplo 2 da sua pergunta resulta em um eco infinito de NULL, devido a:

Se a substituição não estiver entre aspas duplas, a saída será dividida em palavras usando o parâmetro IFS.

Em outras palavras, o shell espera infinitamente pelo echo, porque o delimitador padrão é ESPAÇO, o eco nunca é concluído. Consulte TLDP: Variáveis ​​internas . Isso deixa um cano pendurado para o catcomando.

Como palpite, acredito que 4 e 5 funcionam devido ao redirecionamento de saída.

eyoung100
fonte