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

9

Informações 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 até o EXEMPLOS na parte inferior, se quiser apenas analisar os exemplos simplificados que eu fiz.

OBSERVAÇÃO: não sou um grande usuário zsh .

Eu estava vendo os fzf keybindings para bash e zsh .

Observe como ambos executam um comando variável $(__fzfcmd) . __fzfcmd por padrão envia fzf para stdout e a substituição de parâmetro apenas executa o comando ( fzf ) resultante da saída.

Uma diferença entre o script bash e zsh é que o bash canaliza ainda mais a saída de $(__fzfcmd) , mas zsh apenas captura-o dentro de uma matriz. Meu palpite é por causa de um problema em zsh quando você canaliza ainda mais a saída de fzf , onde você não pode inserir fzf e o processo enviado por fzf não recebe nenhum stdin. Sua única opção é ^Z ou ^C . ^C parece basear o processo por algum motivo. Ou talvez eles apenas o quisessem em uma matriz para que pudessem executar zle vi-fetch-history em isso . A versão bash faz alguma mágica na associação de chaves com "\e^": history-expand-line

Agora fzf não é importante. Parece que você só precisa de um programa que produza o tty para 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 que saem para o tty que pode causar esse problema em zsh :

  • vipe (execute o editor no meio de um pipe)
  • 'vim -' (faz o vim ler o stdin. similar ao vipe, mas não sai para o stdout)

Nos exemplos abaixo, substitua todas as ocorrências de vipe por vim - se você não quiser fazer uma instalação separada. Lembre-se de que vim - não exibe o conteúdo do editor como stdout vipe .

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 principalmente imaginando por que 2) tem um problema para zsh , mas não para bash e por que 4) e 5) corrigem o problema de zsh .

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

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

ATUALIZAÇÃO

Eu adicionei outra solução alternativa que não causa zsh para ter esse problema, 5) . É semelhante a 4) , mas em vez de redirecionar stdout diretamente para stin , eu o redireciono para um arquivo que redireciona para stdin usando a substituição do processo.

    
por dosentmatter 18.12.2017 / 23:44

1 resposta

0

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

Citações de zsh: 14 Expansão

A command enclosed in parentheses preceded by a dollar sign, like $(...), or quoted with grave accents, like ‘...’, is replaced with its standard output, with any trailing newlines deleted. If the substitution is not enclosed in double quotes, the output is broken into words using the IFS parameter. The substitution $(cat foo) may be replaced by the equivalent but faster $(<foo). In either case, if the option GLOB_SUBST is set, the output is eligible for filename generation.

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

If the substitution is not enclosed in double quotes, the output is broken into words using the IFS parameter.

Em outras palavras, o shell aguarda infinitamente o echo , porque o delimitador padrão é SPACE, o eco nunca é concluído. Veja TLDP: Internal Variables . Isso deixa um pipe suspenso para o comando cat .

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

    
por 27.06.2018 / 03:51

Tags