Invocando o vi por find | xargs quebra meu terminal. Por quê?

123

Ao invocar vim a find | xargs , assim:

find . -name "*.txt" | xargs vim

você recebe um aviso sobre

Input is not from a terminal

e um terminal com comportamento muito quebrado depois. Por que isso?

    
por DevSolar 15.09.2011 / 18:26

5 respostas

85

Quando você chama um programa via xargs , o stdin do programa (entrada padrão) aponta para /dev/null . (Como o xargs não conhece o stdin original , ele faz a próxima melhor coisa.)

$ true | xargs filan -s
    0 chrdev /dev/null
    1 tty /dev/pts/1
    2 tty /dev/pts/1

$ true | xargs ls -l /dev/fd/

O Vim espera que seu stdin seja o mesmo que seu terminal de controle, e executa vários ioctl relacionados ao terminal está em stdin diretamente. Quando feito em /dev/null (ou qualquer outro descritor de arquivo não-tty), essas ioctls são insignificantes e retornam ENOTTY, que é ignorado silenciosamente.

  • Meu palpite em uma causa mais específica: Na inicialização, o Vim lê e lembra as configurações antigas do terminal e restaura-as novamente ao sair. Em nossa situação, quando as "configurações antigas" são solicitadas para um não-tty fd (descritor de arquivo), o Vim recebe todos os valores vazios e todas as opções desabilitadas, e descuidadamente define o mesmo para o seu terminal.

    Você pode ver isso executando vim < /dev/null , saindo dele e executando stty , que produzirá um lote inteiro de <undef> s. No Linux, executar stty sane tornará o terminal utilizável novamente (embora tenha perdido opções como iutf8 , possivelmente causando pequenos aborrecimentos mais tarde).

Você poderia considerar isso um bug no Vim, já que pode abrir /dev/tty para controle de terminal, mas não. (Em algum momento durante a inicialização, o Vim duplica seu stderr para stdin, o que permite que ele leia seus comandos de entrada - de um fd aberto para gravação - mas mesmo isso não é feito cedo o suficiente.)

    
por 15.09.2011 / 18:41
122

Seguindo a resposta de Grawity, xargs points stdin to /dev/null

Do OSX / BSD man xargs

-o      Reopen stdin as /dev/tty in the child process 
        before executing the command.  This is useful 
        if you want xargs to run an interactive application.

Assim, a seguinte linha de código deve funcionar para você:

find . -name "*.txt" | xargs -o vim

Para o% GNUman xargs não há sinalizador, mas podemos explicitamente passar em / dev / tty para resolver o problema:

find . -name "*.txt" | xargs bash -c '</dev/tty vim "$@"' ignoreme

o ignoreme está lá para pegar $ 0, então $ @ é todos os argumentos do xargs

    
por 23.05.2012 / 14:15
29

A maneira mais fácil:

vim $(find . -name "*foo*")
    
por 08.03.2014 / 03:13
20

Ele deve funcionar bem se você usar a opção -exec em find ao invés de canalizar em xargs. Por exemplo,

$ find . -type f -name filename.txt -exec vi {} +

    
por 10.03.2013 / 15:31
8

Use o GNU Parallel em vez disso:

find . -name "*.txt" | parallel -j1 --tty vim

Ou se você deseja abrir todos os arquivos de uma só vez:

find . -name "*.txt" | parallel -Xj1 --tty vim

Até lida corretamente com nomes de arquivos como:

My brother's 12" records.txt

Assista ao vídeo de introdução para saber mais: link

    
por 16.09.2011 / 19:45

Tags