¿Cómo usar la expansión de retroceso para poblar el arglista?


11

De ayuda :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.

Si escribo el comando tomado de la ayuda, aparece un error:

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

¿Por qué el ejemplo de la ayuda usa dos barras invertidas en lugar de una y por qué no funciona?

El siguiente trabajo:

  • Si elimino una de las dos barras diagonales inversas que evitan que la estrella se expanda antes del findprograma:

    :next `find . -name ver\*.c -print`
    
  • Si elimino ambas barras invertidas y pongo comillas simples alrededor del patrón ver*.c:

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

Hasta este punto, la regla parece ser:
si su comando de shell contiene una estrella y no desea que el shell lo expanda antes del comando, coloque una barra invertida frente a él o ponga comillas simples alrededor del patrón .

Pero la ayuda da otro ejemplo:

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

Este comando funciona sin ninguna modificación, sin necesidad de comillas simples, sin necesidad de barra invertida.
Supongo que la razón por la que funciona es porque el lscomando (al contrario del -nameargumento del findcomando) acepta múltiples argumentos de archivo y no ve ningún problema con la expansión del shell *.patch.

Ahora, digamos que yo quiero para buscar todos los archivos con la extensión .confdentro de la /etccarpeta y canaliza el resultado de findque greppara obtener sólo los partidos que contienen la cadena input.
En el shell, desde cualquier directorio de trabajo, escribiría:

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

Y funcionaría.

En vim, escribiré el mismo comando poniendo marcas de retroceso a su alrededor, y una barra invertida frente al símbolo de tubería para evitar que vim lo interprete como una terminación de comando:

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

Y funciona.

Ahora, si escribo el mismo comando sin la tubería y el grep input, obtengo un error:

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

¿Por qué hay un error en este caso a pesar de que protegí la estrella con comillas simples?
¿Y por qué hay un error ahora, pero no solo antes con la tubería y el grep input?

Para intentar comprender, he creado un comando más simple:

find . -name '*.conf'

Busca todos los archivos con la extensión .confen el directorio de trabajo. El comando funciona en el shell.

Para probarlo en vim, escribí: :next `find . -name '*.conf'`
Y funciona. En este caso, el punto representa mi directorio de trabajo actual como lo muestra el comando Ex, :pwdque es mi directorio de inicio /home/usernamedesde que inicié la sesión vim desde él.

¿Por qué funciona cuando solicito buscar en el directorio de trabajo actual, mientras que no funciona cuando solicito buscar en una carpeta arbitraria como /etc?

Ahora, si cambio mi directorio de trabajo de /home/usernamea /etccon el comando vim Ex :cd /etcy reintento el mismo comando que antes, nuevamente se produce un error:

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

¿Por qué funciona el mismo comando cuando estoy en mi carpeta de inicio pero no cuando estoy dentro /etc?

Estoy seguro de que hay algo de lógica, pero no puedo encontrar una.

¿Cuál es la sintaxis general y correcta para llenar el arglista con un comando de shell arbitrario (que contiene una estrella, una tubería, buscando en cualquier directorio desde cualquier directorio de trabajo)?

Estoy usando la versión vim 7.4.942 y zsh es mi shell predeterminado. Probé estos comandos con un mínimo de inicializaciones ( vim -u NORC -N) tanto de bash como de zsh.

¿Necesito configurar vim para llamar a bash y no a zsh?


Me pregunto si trataste de iniciar vim con todos esos archivos pasados ​​como argumentos. Me refiero a algo como:vim $(find . -name ver*.c)
Vlad GURDIGA

@VladGURDIGA He intentado y todos trabajan como se espera de la cáscara: 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 Pero me gustaría saber cómo poblar el arglista sin salir de la sesión actual, ya que generalmente solo tengo uno. También podría moverme en el shell, buscar un grupo de archivos y enviarlos a un servidor Vim (con el argumento --remote, ver: vi.stackexchange.com/a/5618/4939 ) pero tengo curiosidad por saber cómo hazlo directamente desde la sesión actual. Si no es posible, está bien, pero después de leer la ayuda, parece que se puede hacer. Si es así, me gustaría entender por qué vim reacciona de manera tan diferente a comandos similares.
saginaw

Oye, parece que en el primer comando de muestra te estás perdiendo el toque de retroceso de cierre. ;) Sin embargo, aparte de eso, supongo que el ejemplo de la ayuda usa dos barras invertidas en lugar de una para evitar la expansión del shell, como se explica aquí: unix.stackexchange.com/q/104260/23669 .
Vlad GURDIGA

En el caso de que find /etc -name '*.conf'no funcione, creo que algunos nombres divertidos salen de ese comando, y esta puede ser la razón por la que funcionó cuando se transmitió grep.
Vlad GURDIGA

Respuestas:


6

Todavía no sé cómo usar la expansión de retroceso para llenar el arglista con un comando de shell arbitrario, sin embargo, he encontrado una solución.

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.

Entonces, en lugar de expandir directamente un comando de shell como este:

:args `shell command`

Podemos enviar el comando de shell a la función Vim systemlist()y usar el registro de expresión =para expandir la expresión resultante de esta manera:

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

Para guardar algunas pulsaciones de teclas, he definido el siguiente comando Ex en mi vimrc ( :PApara Populate Arglist):

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

Y lo probé con varios comandos de 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

Hasta ahora funciona como se esperaba y el comando se puede escribir como lo haría en el shell (no es necesario escapar de la tubería, por ejemplo).
Parece más consistente y confiable que la expansión directa de backtick.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.