Wie geht man mit Dateinamen um, die ein einfaches Anführungszeichen enthalten, innerhalb einer Zsh-Vervollständigungsfunktion?

Wie geht man mit Dateinamen um, die ein einfaches Anführungszeichen enthalten, innerhalb einer Zsh-Vervollständigungsfunktion?

Ich verwende die zshShell und habe den folgenden Code darin .zshrc:

fpath=(~/.zsh/completion $fpath)
autoload -Uz _vc

autoload -Uz compinit
compinit

Die erste Zeile fügt den Pfad ~/.zsh/completionzum Array hinzu, das in der Umgebungsvariablen gespeichert ist fpath.

Die zweite Zeile lädt eine Vervollständigungsfunktion namens _vcfür eine benutzerdefinierte Shell-Funktion namens vc. Der Zweck von vcbesteht einfach darin, eine Datei in einem bestimmten Ordner ( ) zu bearbeiten ~/.cheat. _vcist in der Datei definiert ~/.zsh/completion/_vc.

Die letzten beiden Zeilen aktivieren ein Vervollständigungssystem.

Hier ist der Code für meine Vervollständigungsfunktion _vc:

#compdef vc

declare -a cheatsheets
cheatsheets="$(ls ~/.cheat)"
_arguments "1:cheatsheets:(${cheatsheets})" && return 0

Ich habe es hiervon kopiertAdresse, und habe es an meine Bedürfnisse angepasst.

Solange das Verzeichnis ~/.cheatkeine Datei enthält, deren Name ein einfaches Anführungszeichen enthält, funktioniert die Vervollständigung. Wenn jedoch eines vorhanden ist, z. B. foo'bar, schlägt die Vervollständigung mit dieser Fehlermeldung fehl:

(eval):56: unmatched '
(eval):56: unmatched '
(eval):56: unmatched '
(eval):56: unmatched '

Ich habe eine Lösung gefunden, indem ich die doppelten Anführungszeichen in der Zeile cheatsheets="$(ls ~/.cheat)"durch einfache Anführungszeichen ersetzt habe cheatsheets='$(ls ~/.cheat)'.

TabWenn ich jetzt nach meinem Befehl drücke vc, schlägt zsh darin enthaltene Dateien vor ~/.cheat, einschließlich foo\'bar(zsh scheint das einfache Anführungszeichen automatisch maskiert zu haben).

Ich verstehe jedoch nicht, wie oder warum es funktioniert. Mit einfachen Anführungszeichen cheatsheetssollte die Variable eine wörtliche Zeichenfolge enthalten. Die $(...)Befehlsersetzung sollte also nicht erweitert werden. Wenn ich beispielsweise die folgenden Befehle ausführe:

myvar='$(ls)'
echo $myvar    →    outputs `$(ls)` literally

Warum wird also '$(ls ~/.cheat)'innerhalb erweitert _arguments "1:cheatsheets:(${cheatsheets})" && return 0? Und warum wird das einfache Anführungszeichen innerhalb automatisch maskiert foo'bar?

Antwort1

Wenn wir darauf bestehen, die Dinge auf die falsche Art und Weise zu tun™

#compdef vc

declare -a cheatsheets
cheatsheets=(${(f)"$(ls ~/.cheat/)"})
_arguments '1:cheatsheets:(${cheatsheets})' && return 0

Igitt! Das wird natürlich nicht funktionieren, wenn ein Dateiname eine neue Zeile enthält, da (f)diese aufgeteilt wird.Parsing lsist sowieso eine schlechte Idee; ZSH kann Dateien direkt in ein Array globben:

% mkdir ~/.lojban
% touch ~/.lojban/{go\'i,na\ go\'i}
% words=(~/.lojban/*) 
% print -l ${words:t}
go'i
na go'i
% words=(~/.lojban/*(On))
% print -l ${words:t}    
na go'i
go'i
% 

Aber wir brauchen wahrscheinlich kein lokales Array; _fileswir können es auf einem Glob vervollständigen:

#compdef vc
_arguments '1:cheatsheets:_files -g "~/.cheat/*"' && return 0

Dies gibt vollständig qualifizierte Dateipfade zurück. Wenn nur der reine Dateiname aus einem Suchverzeichnis benötigt wird, können wir stattdessen Folgendes verwenden:

#compdef vc
_arguments '1:cheatsheets:_files -W ~/.cheat' && return 0

verwandte Informationen