Como usar a expansão de backtick para preencher o arglist?


11

Da ajuda :help backtick-expansion:

On Unix and a few other systems you can also use backticks for the file name
argument, for example:
    :next `find . -name ver\\*.c -print`
    :view `ls -t *.patch  \| head -n1`
The backslashes before the star are required to prevent the shell from
expanding "ver*.c" prior to execution of the find program.  The backslash
before the shell pipe symbol "|" prevents Vim from parsing it as command
termination.

Se eu digitar o comando retirado da ajuda, recebo um erro:

:next `find . -name ver\\*.c -print
E79: Cannot expand wildcards  

Por que o exemplo da ajuda usa duas barras invertidas em vez de uma e por que não funciona?

O seguinte trabalho:

  • Se eu remover uma das duas barras invertidas que protegem a estrela de ser expandida pelo shell antes do findprograma:

    :next `find . -name ver\*.c -print`
    
  • Se eu remover as duas barras invertidas e colocar aspas simples em torno do padrão ver*.c:

    :next `find . -name 'ver*.c' -print`
    

Até esse ponto, a regra parece ser:
se o seu comando shell contiver uma estrela e você não quiser que o shell o expanda antes do comando, coloque uma barra invertida na frente ou aspas simples ao redor do padrão .

Mas a ajuda dá outro exemplo:

:view `ls -t *.patch  \| head -n1`

Este comando funciona sem nenhuma modificação, sem necessidade de aspas simples, sem necessidade de barra invertida.
Suponho que o motivo pelo qual ele funcione é porque o lscomando (ao contrário do -nameargumento do findcomando) aceita vários argumentos de arquivo e não vê nenhum problema com o shell em expansão *.patch.

Agora, digamos que eu quero olhar para todos os arquivos com a extensão .confdentro da /etcpasta e envia a saída de findpara grepde obter apenas os jogos contêm a cadeia input.
No shell, em qualquer diretório de trabalho, digitei:

find /etc -name '*.conf' | grep input

E funcionaria.

No vim, digitarei o mesmo comando colocando barras em volta dele e uma barra invertida na frente do símbolo de pipe para impedir que o vim interprete-o como uma finalização de comando:

:next `find /etc -name '*.conf' \| grep input`

E isso funciona.

Agora, se eu digitar o mesmo comando sem o pipe e o grep input, recebo um erro:

:next `find /etc -name '*.conf'`
E79: Cannot expand wildcards

Por que existe um erro neste caso, mesmo que eu tenha protegido a estrela com aspas simples?
E por que há um erro agora, mas não antes com o pipe e o grep input?

Para tentar entender, criei um comando mais simples:

find . -name '*.conf'

Ele procura todos os arquivos com a extensão .confno diretório de trabalho. O comando funciona no shell.

Para testá-lo no vim, digitei: :next `find . -name '*.conf'`
E funciona. Nesse caso, o ponto representa meu diretório de trabalho atual, conforme exibido pelo comando Ex, :pwdque é o meu diretório pessoal /home/usernamedesde que iniciei a sessão do vim.

Por que funciona quando solicito pesquisar no diretório de trabalho atual, enquanto não funciona quando solicito pesquisar em uma pasta arbitrária como /etc?

Agora, se eu mudar meu diretório de trabalho de /home/usernamepara para /etccom o comando vim Ex :cd /etce tentar novamente o mesmo comando de antes, novamente ocorrerá um erro:

:next `find . -name '*.conf'`
E79: Cannot expand wildcards

Por que o mesmo comando funciona quando estou na minha pasta pessoal, mas não quando estou /etc?

Tenho certeza de que há alguma lógica, mas não consigo encontrar uma.

Qual é a sintaxe geral e correta para preencher o arglist com um comando de shell arbitrário (contendo uma estrela, um pipe, pesquisando em qualquer diretório de qualquer diretório ativo)?

Estou usando a versão 7.4.942 do vim e o zsh é meu shell padrão. Testei esses comandos com um mínimo de inicializações ( vim -u NORC -N) do bash e do zsh.

Preciso configurar o vim para chamar bash e não zsh?


Gostaria de saber se você tentou iniciar o vim com todos esses arquivos passados ​​como argumentos? Quero dizer algo como:vim $(find . -name ver*.c)
Vlad GURDIGA

@VladGURDIGA Eu apenas tentei e todas elas funcionam como esperado a partir do shell: vim $(find . -name ver\*.c -print), vim $(ls -t *.patch | head -n1), vim $(find /etc -name '*.conf' | grep input), vim $(find /etc -name '*.conf'),vim $(find . -name '*.conf')
Saginaw

@VladGURDIGA Mas eu gostaria de saber como preencher o arglist sem sair da sessão atual, pois normalmente tenho apenas uma. Eu também poderia me movimentar no shell, procurar um grupo de arquivos e enviá-los para um servidor Vim (com o argumento --remote, consulte: vi.stackexchange.com/a/5618/4939 ), mas estou curioso para saber como faça isso diretamente da sessão atual. Se não for possível, tudo bem, mas depois de ler a ajuda, parece que isso pode ser feito. Se for, eu gostaria de entender por que o vim reage de maneira tão diferente a comandos semelhantes.
Saginaw

Ei, parece que no primeiro exemplo de comando você está perdendo o backtick de fechamento. ;) Além disso, acho que o exemplo da ajuda usa duas barras invertidas em vez de uma para impedir a expansão do shell, conforme explicado aqui: unix.stackexchange.com/q/104260/23669 .
Vlad GURDIGA

No caso de find /etc -name '*.conf'não funcionar, acho que alguns nomes engraçados saem desse comando e, esse pode ser o motivo pelo qual ele funcionou quando transmitido grep.
Vlad GURDIGA

Respostas:


6

Ainda não sei como usar o backtick-expansion para preencher o arglist com um comando shell arbitrário, no entanto, encontrei uma solução alternativa.

De :help `=:

You can have the backticks expanded as a Vim expression, instead of as an
external command, by putting an equal sign right after the first backtick,
e.g.:
    :e `=tempname()`
The expression can contain just about anything, thus this can also be used to
avoid the special meaning of '"', '|', '%' and '#'.  However, 'wildignore'
does apply like to other wildcards.

Então, em vez de expandir diretamente um comando shell como este:

:args `shell command`

Podemos enviar o comando shell para a função Vim systemlist()e usar o registrador de expressão =para expandir a expressão resultante assim:

:args `=systemlist("shell command")`

Para salvar algumas teclas, defini o seguinte comando Ex no meu vimrc ( :PApara Populate Arglist):

command! -nargs=1 PA args `=systemlist(<q-args>)`

E testou-o com vários comandos do shell:

:PA find . -name ver\*.c -print 2>/dev/null
:PA find . -name 'ver*.c' -print 2>/dev/null
:PA ls -t *.patch  | head -n1
:PA find /etc -name '*.conf' 2>/dev/null | grep input
:PA find /etc -name '*.conf' 2>/dev/null
:PA find . -name '*.conf' 2>/dev/null

Até agora, ele funciona como o esperado e o comando pode ser digitado como seria no shell (não é necessário escapar do cano, por exemplo).
Parece mais consistente e confiável do que a expansão direta do backtick.

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.