Wie verwende ich die Backtick-Erweiterung, um die Arglist zu füllen?

11

Von der Hilfe :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.

Wenn ich den Befehl aus der Hilfe eingebe, wird folgende Fehlermeldung angezeigt:

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

Warum verwendet das Beispiel aus der Hilfe zwei Backslashes anstelle von einem und warum funktioniert es nicht?

Die folgende Arbeit:

  • Wenn ich einen der beiden Backslashes entferne, die den Stern vor der Erweiterung durch die Shell vor dem findProgramm schützen :

    :next `find . -name ver\*.c -print`
    
  • Wenn ich beide Backslashes entferne und einfache Anführungszeichen um das Muster setze ver*.c:

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

Bis zu diesem Punkt scheint die Regel zu lauten:
Wenn Ihr Shell-Befehl einen Stern enthält und Sie nicht möchten, dass die Shell ihn vor dem Befehl erweitert, setzen Sie einen Backslash davor oder setzen Sie einfache Anführungszeichen um das Muster .

Aber die Hilfe gibt ein anderes Beispiel:

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

Dieser Befehl funktioniert ohne Änderungen, ohne einfache Anführungszeichen und ohne umgekehrten Schrägstrich.
Ich nehme an, der Grund, warum es funktioniert, ist, dass der lsBefehl (im Gegensatz zum -nameArgument des findBefehls) mehrere Dateiargumente akzeptiert und kein Problem mit der Erweiterung der Shell sieht *.patch.

Nun lassen Sie uns sagen , dass ich für alle Dateien mit der Erweiterung aussehen wollen .confin den /etcOrdner und Rohren die Ausgabe findauf grepnur die Spiele zu bekommen die Zeichenfolge enthält input.
In die Shell würde ich aus jedem Arbeitsverzeichnis Folgendes eingeben:

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

Und es würde funktionieren.

In vim gebe ich denselben Befehl ein, indem ich Backticks um ihn herum und einen Backslash vor dem Pipe-Symbol einfüge, um zu verhindern, dass vim ihn als Befehlsbeendigung interpretiert:

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

Und es funktioniert.

Wenn ich jetzt denselben Befehl ohne Pipe und Pipe grep inputeingebe, wird folgende Fehlermeldung angezeigt:

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

Warum liegt in diesem Fall ein Fehler vor, obwohl ich den Stern mit einfachen Anführungszeichen geschützt habe?
Und warum gibt es jetzt einen Fehler, aber nicht erst vorher mit der Pipe und der grep input?

Zum besseren Verständnis habe ich mir einen einfacheren Befehl ausgedacht:

find . -name '*.conf'

Es sucht nach allen Dateien mit der Erweiterung .confim Arbeitsverzeichnis. Der Befehl funktioniert in der Shell.

Um es in vim zu testen, habe ich Folgendes eingegeben: :next `find . -name '*.conf'`
Und es funktioniert. In diesem Fall steht der Punkt für mein aktuelles Arbeitsverzeichnis, wie es vom Befehl Ex angezeigt wird. Dies :pwdist mein Ausgangsverzeichnis, /home/usernameseit ich die vim-Sitzung von dort aus gestartet habe.

Warum funktioniert es, wenn ich nach dem aktuellen Arbeitsverzeichnis frage, während es nicht funktioniert, wenn ich nach einem beliebigen Ordner wie z /etc.

Wenn ich nun mein Arbeitsverzeichnis mit dem Befehl vim Ex von /home/usernameauf ändere und denselben Befehl wie zuvor erneut versuche, tritt erneut ein Fehler auf:/etc:cd /etc

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

Warum funktioniert der gleiche Befehl, wenn ich in meinem Home-Ordner bin, aber nicht, wenn ich in bin /etc?

Ich bin mir sicher, dass es eine Logik gibt, aber ich kann keine finden.

Wie lautet die allgemeine und korrekte Syntax, um die Arglist mit einem beliebigen Shell-Befehl zu füllen (der einen Stern oder eine Pipe enthält und in einem beliebigen Verzeichnis aus einem beliebigen Arbeitsverzeichnis sucht)?

Ich verwende vim Version 7.4.942 und zsh ist meine Standard-Shell. Ich habe diese Befehle mit einem Minimum an Initialisierungen ( vim -u NORC -N) sowohl von bash als auch von zsh getestet .

Muss ich vim so konfigurieren, dass bash und nicht zsh aufgerufen wird?

saginaw
quelle
Ich frage mich, ob Sie versucht haben, vim mit all den Dateien zu starten, die als Argumente übergeben wurden. Ich meine so etwas wie:vim $(find . -name ver*.c)
Vlad GURDIGA
@VladGURDIGA Ich habe gerade versucht , und sie alle arbeiten , wie aus der Schale zu erwarten: 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 Aber ich würde gerne wissen, wie man den Arglist auffüllt, ohne die aktuelle Sitzung zu beenden, da ich normalerweise nur eine habe. Ich könnte mich auch in der Shell bewegen, nach einer Gruppe von Dateien suchen und sie an einen Vim-Server senden (mit dem Argument --remotesiehe: vi.stackexchange.com/a/5618/4939 ), aber ich bin gespannt, wie es geht Mach es direkt aus der aktuellen Sitzung. Wenn es nicht möglich ist, ist das in Ordnung, aber nach dem Lesen der Hilfe scheint es möglich zu sein. Wenn ja, würde ich gerne verstehen, warum vim auf ähnliche Befehle so unterschiedlich reagiert.
Saginaw
Hey, es scheint, als ob Ihnen im ersten Beispielbefehl das schließende Backtick fehlt. ;) Abgesehen davon denke ich, dass das Beispiel aus der Hilfe zwei Backslashes anstelle von einem verwendet, um eine Shell-Erweiterung zu verhindern, wie hier erläutert: unix.stackexchange.com/q/104260/23669 .
Vlad GURDIGA
Im Fall der find /etc -name '*.conf'nicht funktioniert, würde denken , dass ich ein paar lustigen Namen dieses Befehl kommen, und dies ist der Grund sein, warum es funktionierte , als geleitet durch grep.
Vlad GURDIGA

Antworten:

6

Ich weiß immer noch nicht, wie ich die Backtick-Erweiterung verwenden soll, um die Arglist mit einem beliebigen Shell-Befehl zu füllen, aber ich habe eine Problemumgehung gefunden.

Von :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.

Anstatt einen Shell-Befehl wie diesen direkt zu erweitern:

:args `shell command`

Wir können den Shell-Befehl an die Vim-Funktion senden systemlist()und das Ausdrucksregister verwenden =, um den resultierenden Ausdruck wie folgt zu erweitern:

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

Um einige Tastenanschläge zu speichern, habe ich in meinem vimrc ( :PAfür Populate Arglist) den folgenden Ex-Befehl definiert :

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

Und testete es mit verschiedenen Shell-Befehlen:

: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

Bisher funktioniert es wie erwartet und der Befehl kann wie in der Shell eingegeben werden (es ist beispielsweise nicht erforderlich, die Pipe zu verlassen).
Es scheint konsistenter und zuverlässiger zu sein als die direkte Backtick-Erweiterung.

saginaw
quelle