Como chamar o editor vim e a saída de pipe para o bash

34

Às vezes, preciso escrever um texto e depois canalizá-lo para outro comando. Meu fluxo de trabalho usual é mais ou menos assim:

vim
# I edit and save my file as file.txt
cat file.txt | pandoc -o file.pdf # pandoc is an example 
rm file.txt

Acho isso complicado e procurando aprender scripts bash, gostaria de tornar o processo muito mais simples, escrevendo um comando que aciona um editor e, quando o editor fecha, direciona a saída do editor para o stdout. Então eu seria capaz de executar o comando como quickedit | pandoc -o file.pdf.

Não tenho certeza de como isso funcionaria. Eu já escrevi uma função para automatizar isso, seguindo o fluxo de trabalho exato acima, além de algumas adições. Ele gera uma sequência aleatória para atuar como um nome de arquivo e a passa para o vim quando a função é chamada. Quando o usuário sai do vim salvando o arquivo, a função imprime o arquivo no console e exclui o arquivo.

function quickedit {
    filename="$(cat /dev/urandom | env LC_CTYPE=C tr -cd 'a-f0-9' | head -c 32)"
    vim $filename
    cat $filename
    rm $filename
}
# The problem:
# => Vim: Warning: Output is not to a terminal

O problema que encontrei logo é que, quando faço algo como o quickedit | commandpróprio vim, não pode ser usado como editor, porque toda a saída é restrita ao canal.

Gostaria de saber se existem soluções alternativas para isso, para que eu possa canalizar a saída da minha quickeditfunção. A alternativa subótima é acionar um editor separado, digamos texto sublime, mas eu realmente quero ficar no terminal.

theideasmith
fonte
No vim, emita o comando :w !pandoc -o file.pdf? (Nota: o espaço entre we !é essencial.) #
1010 John1024
Ok - isso é ótimo. Eu não sabia que você poderia canalizar a saída do vim a partir do próprio vim. Isso serve aos meus propósitos, mas, para meu conhecimento futuro, é possível resolver meu problema exatamente como pretendia que fosse resolvido?
Theideasmith # 10/16
Você pode postar sua função? O que você quer dizer quando fala sobre o problema que encontrou em breve (segundo último parágrafo)? Eu não entendo isso.
Lucas
5
Como um aparte, você deve usá-mktemp lo em vez de reinventá-lo de maneira insegura.
Wildcard
Como um novo usuário do bash, quais são os perigos de segurança ao reinventar o mktemp?
Theideasmith # 10/16

Respostas:

42

vipe é um programa para editar pipelines:

command1 | vipe | command2

Você obtém um editor com a saída completa de command1e, quando você sai, o conteúdo é passado para command2o canal.

Nesse caso, não há command1. Então, você poderia fazer:

: | vipe | pandoc -o foo.pdf

Ou:

vipe <&- | pandoc -o foo.pdf

vipeseleciona as variáveis EDITORe VISUAL, para que você possa usá-las para abrir o Vim.

Se você não o instalou, vipeestá disponível no moreutilspacote; sudo apt-get install moreutils, ou qualquer que seja o equivalente do seu sabor.

muru
fonte
11
Para usuários de Mac que chegaram aqui: moreutilsestá disponível no Homebrew.
vergenzt
Para usuários do FreeBSD: moreutilsestá disponível para instalação via pkg (8) .
Mateusz Piotrowski
20

Você pode fazer isso no Vim:

:w !pandoc -o file.pdf

Ou até mesmo grave o buffer em um pipeline complexo:

:w !grep pattern | somecommand > file.txt

E então você pode sair do Vim sem salvar:

:q!

No entanto, considerando seu caso de uso específico, provavelmente há uma solução melhor usando vicomo seu editor de linha de comando. Supondo que você use bash:

set -o vi

Isso define suas combinações de teclas para vi. Assim você pode editar seus comandos à direita na linha de comando com base vikeybindings pressionando <Esc>e digitando vicomandos como x, cw, etc. (Você pode voltar no modo de inserção pressionando i.)

Ainda melhor e mais relevante para essa pergunta, você pode abrir o Vim para criar diretamente o conteúdo da linha de comando. Basta digitar <Esc>ve você obterá um buffer Vim vazio. Quando você salva e sai, esse é o comando na sua linha de comandos e é executado imediatamente. Isso é muito mais flexível do que editar diretamente na linha de comando, pois você pode escrever um mini-script inteiro, se desejar.


Portanto, por exemplo, se você quiser escrever um texto complicado e inseri-lo no pandoc imediatamente, basta digitar:

<Esc>v

Em seguida, edite o buffer do Vim até obter algo como:

cat <<EOF | pandoc -o file.pdf
stuff for pandoc
more stuff for pandoc
EOF

Em seguida, salve e saia (com :x) e a coisa toda será executada como um comando shell.

Também estará disponível no histórico de comandos do seu shell.

Curinga
fonte
11
Se você preferir manter as ligações de teclas do Emacs, ainda poderá usar o Vim para editar linhas de comando, definindo a EDITORvariável de ambiente vime pressionando Ctrl-Xseguido por Ctrl-Eenquanto edita a linha de comando.
Anthony G - Justice para Monica
18

Executando em um pipeline

Experimentar:

quickedit() (  trap 'rm ~/temp$$' exit; vim ~/temp$$ >/dev/tty; cat ~/temp$$ )

A chave é que, para poder usar vimnormalmente, o vimstdout precisa ser o terminal. Conseguimos isso aqui com o redirecionamento >/dev/tty.

Por questões de segurança, coloquei o arquivo temporário no diretório inicial do usuário. Para mais informações, consulte a Pergunta 062 de Greg's FAQ . Isso elimina a necessidade de usar um nome de arquivo obscuro.

Exemplo:

Quando vimabre, digito This function succeeded.e salvo o arquivo. O resultado na tela se parece com:

$ quickedit | grep succeeded
This function succeeded.

Embora a saída de quickeditseja redirecionada para um pipeline, vimainda funciona normalmente porque fornecemos acesso direto a ela /dev/tty.

Executando um programa no vim

Como mencionei nos comentários, o vim pode canalizar um arquivo para um comando. No vim, por exemplo, emita o comando :w !pandoc -o file.pdf(Nota: o espaço entre w e! É essencial).

John1024
fonte
2
Boa resposta. Este aborda diretamente a pergunta do usuário e oferece uma solução rápida e elegante: redirecione o vim para / dev / tty! Simples!
Mike S
Eu estou usando o Zsh; trap '...' exitfalha mas trap '...' EXITparece funcionar. Não sei muito sobre interceptação, mas minha abordagem geral para arquivos temporários é usar mktmp [--suffix=...]. Além disso, vim <outfile> -c '...' >/dev/ttyé executado normalmente até o arquivo ser carregado e, em seguida, executa a cadeia de comandos fornecida, inclusive :wqse você deseja pular a fase de edição. Eu também usei set | grep -a EXITe encontrei signals=(EXIT ... DEBUG).
John P
5

Certifique-se de que vimestá definido como seu editor padrão (por exemplo, export EDITOR=vimno seu .bash_profileou .bashrc. Então, a qualquer momento, você pode digitar Ctrl- Xseguido de Ctrl- E. Isso abrirá sua linha de comando atual no seu editor configurado (por exemplo vim). Faça suas edições, salve e exit e o comando será executado como se você o tivesse digitado na linha de comando, incluindo pipelines e similares.

DopeGhoti
fonte