O terminal funcionou após a chamada do Vim com xargs


30

Às vezes, tentei invocar o Vim usando xargsassim:

find . -name '*.java' | xargs vim

… Que tipo de trabalho:

  1. Quando o Vim é iniciado, vejo o seguinte aviso brevemente:

    Vim: Warning: Input is not from a terminal
    
  2. A edição funciona - :filesenumera corretamente todos os .javaarquivos conforme o esperado.
  3. Eu posso salvar e sair.

No entanto, depois de sair do Vim, meu terminal é acionado:

  • Tudo o que eu digito no prompt do shell não é ecoado.
  • Os retornos de carro não aparecem e os feeds de linha aparecem apenas algumas vezes.

Isso continua até eu emitir um reset(1)comando para reinicializar o terminal.

Isso é um bug do Vim ou existe uma explicação mais satisfatória para o motivo de ele interagir com o terminal assim? Eu já vi isso acontecer no Vim até a versão 7.3 (a versão não parece importar) no Linux e em vários Unices.

Estou ciente de uma solução alternativa, a saber vim $(find . -name '*.java'). Outras soluções alternativas seriam bem-vindas, embora essa não seja minha pergunta principal.


Eu meio que desejo que você preencha este site com o maior número possível de perguntas, mas… essa pergunta foi respondida dezenas de vezes ao longo dos anos no SO / SU e em outros lugares: xargsusa um manequim stdinque não pode ser usado pelo Vim e quebras tudo depois.
Romainl

1
@romainl Não sou ativo nesses sites e nunca o vi perguntado lá - honestamente.
200_success 5/02

Relacionado: superuser.com/questions/336016/… - talvez alguém possa condensar as principais respostas para uma ótima resposta.
muru

2
Ótima pergunta - isso já aconteceu comigo várias vezes. Além disso, eu não uso nenhum outro site de SO - se for relacionado ao vim, gostaria de encontrá-lo aqui.
craigp 05/02

Respostas:


21

Isso acontece quando o vim é chamado e conectado à saída do pipeline anterior, em vez do terminal e recebendo diferentes entradas inesperadas (como NULs). O mesmo acontece quando você executa :, vim < /dev/nullportanto, o resetcomando neste caso ajuda. Isso é bem explicado pelo grawity no superusuário .

Se você estiver usando findpara passar nomes de arquivos para editar, não precisa xargs, basta usar -exec, por exemplo:

find . -name '*.java' -exec vim {} +

Se você deseja usar xargs, no Unix / macOS, use o -oparâmetro, como:

find . -name '*.java' | xargs -o vim

-oReabra stdin como / dev / tty no processo filho antes de executar o comando. Isso é útil se você deseja que o xargs execute um aplicativo interativo.

ou:

find . -name "*.java" -type f -print0 | xargs -o -0 vim

Nota: O uso -print0/ -0suportará nomes de arquivos com espaços em branco.


find+ BSDxargs

No Unix / macOS, você pode tentar a seguinte solução alternativa:

find . -name '*.java' | xargs -J% sh -c 'vim < /dev/tty $@'

Você também pode usar a sintaxe de substituição de comando , por exemplo:

vim $(find . -name '*.java')
vim `find . -name '*.java`

Como alternativa, use o GNU em parallelvez de xargsforçar a alocação de tty, no exemplo:

find . -name '*.java' | parallel -X --tty vi

Nota: parallelno Unix / OSX não funcionará, pois possui parâmetros diferentes e não suporta tty.

Muitos outros comandos populares também fornecem alocação pseudo-tty (como -tem ssh), portanto, procure ajuda.

Outra sugestão seria usar:

Relacionado:


Meu GNU xargsnão tem -J. Essa parece ser uma opção do MacOS (BSD) que não está presente na versão GNU.
Pausado até novo aviso.

12

Sugestão de solução alternativa: use um buffer como navegador do sistema de arquivos

Use o vim -comando para ler uma lista de caminhos do stdin. Vim's :help --explica isso: 1

Comece a editar um novo buffer, que é preenchido com o texto lido de stdin. Os comandos que normalmente seriam lidos no stdin agora serão lidos no stderr. Exemplo:

find . -name "*.c" -print | vim -

Você pode usar gfou Ctrl-wCtrl-fpara navegar para o arquivo no mesmo buffer ou em uma nova janela dividida, respectivamente.

Sugestão de solução alternativa: lista de argumentos

Outra ótima maneira é usar a lista de argumentos do Vim. O :argadd **/*.javacomando preencherá a lista de argumentos do Vim com todos os arquivos java encontrados dentro do diretório atual recursivamente. Você pode usar os botões :nexte :prevpara se mover entre os arquivos.


1 O primeiro -diz ao Vim que você deseja procurar na ajuda uma opção de linha de comando; o segundo é realmente o sinalizador de linha de comando que você está procurando. Leia :help help-contextpara mais truques como este.


8

Além disso reset, você pode tentar:

stty sane

o que também deve tornar seu terminal utilizável novamente.

Veja aqui para explicações. E de alguma forma isso pode ser considerado um mau comportamento do vim, pelo menos o Neovim não tem esse problema no momento.


Isso não responde exatamente à pergunta, mas me ajudou, então +1.
Reintegrar Monica iamnotmaynard

Isso responde à minha pergunta;) Embora depois de ler mais o OP, acho resetque também deveria.
Sridhar Sarnobat 20/06

1

A razão é que xargsdefine stdinpara /dev/null, enquanto vimprecisa stdinser /dev/tty.


xargsSolução BSD (por exemplo, Mac):

echo -e 'file1\nfile2' | xargs -o vim

-odefine o stdinprocesso filho do xarg ( vimneste caso) para dev/tty.


xargsSolução GNU (por exemplo, Linux):

GNU xargsnão tem a -oopção. Em vez disso, você precisará usar uma solução alternativa mais complicada. (Nota: é muito importante ter a zerocorda à direita , não a esqueça.)

echo -e 'file1\nfile2' | xargs bash -c '</dev/tty vim "$@"' zero

Você também pode criar um alias:

alias vimin='xargs bash -c '\''</dev/tty vim "$@"'\'' zero'
echo -e 'file1\nfile2' | vimin

Explicação detalhada da solução GNU xargs

Vamos detalhar passo a passo:

echo -e 'file1\nfile2' | xargs bash -c '</dev/tty vim "$@"' zero

1. xargssimplesmente acrescenta o stdinfinal da string, assim xargsestará executando o seguinte:

    bash -c '</dev/tty vim "$@"' zero file1 file2

2. O formato para bash -cé bash -c 'COMMAND_STRING' $0 $1 $2 etc.

"$@"expande para os parâmetros posicionais "$1", "$2"etc. Ele não inclui "$ 0" porque esse é um parâmetro especial para o nome do script, não um parâmetro posicional. É por isso que precisamos adicionar a sequência fictícia zero(pode ser qualquer sequência) para substituí-la $0. Caso contrário, você perderá o primeiro arquivo.

Então, depois de expandir "$@", você acaba com:

    bash -c '</dev/tty vim file1 file2' 

3. bash -cexecutará o COMMAND_STRING:

    </dev/tty vim file1 file2 

</dev/ttydefine stdincomo /dev/ttypara que vimpossa funcionar no modo interativo.


+1 Isso parece ser uma duplicata da resposta com o maior número de votos, mas não é. O $0espaço reservado (aqui "zero") é a chave.
Pausado até novo aviso.

0

Eu encontrei todas essas respostas faltando. Depois de lutar pela questão do xargs, a maneira mais simples - para mim! - de fazer essas pesquisas e aberturas em uma caixa genérica é a seguinte:

vim -O `grep -lir mySearchTerm *`

Não tenho nenhum problema em usar findwith xargse grep, mas acho a sintaxe irritante. E como um codificador diário que usa vime o terminal como seu IDE e pesquisa arquivos por propriedades constantemente, é isso que eu tenho usado.

Há um tempo e um local para find . -path ./node_modules -prune -o -name '*.js' | xargs grep -i mySearchTermacoplar à abertura de arquivos via vim e outros métodos. Para mim, isso raramente é.

Espero que isso ajude alguém.


2
FWIW, é recomendável não usar a notação de backticks herdados e usá-la $(...). Não é tão importante em sua linha de comando quanto em um script, mas acho que ainda é melhor usá-lo em suas respostas :) (referências aqui e aqui )
statox
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.