Eine robuste Methode in bash besteht darin, in ein Array zu expandieren und nur das erste Element auszugeben:
pattern="*.txt"
files=( $pattern )
echo "${files[0]}" # printf is safer!
(Sie können sogar nur echo $files
einen fehlenden Index als [0] behandeln.)
Dies behandelt sicher Leerzeichen, Tabulatoren, Zeilenumbrüche und andere Metazeichen, wenn die Dateinamen erweitert werden. Beachten Sie, dass die Gebietsschemaeinstellungen das ändern können, was "zuerst" ist.
Sie können dies auch interaktiv mit einer Bash- Vervollständigungsfunktion tun :
_echo() {
local cur=${COMP_WORDS[COMP_CWORD]} # string to expand
if compgen -G "$cur*" > /dev/null; then
local files=( ${cur:+$cur*} ) # don't expand empty input as *
[ ${#files} -ge 1 ] && COMPREPLY=( "${files[0]}" )
fi
}
complete -o bashdefault -F _echo echo
Dadurch wird die _echo
Funktion an die Vervollständigung von Argumenten an den echo
Befehl gebunden (wobei die normale Vervollständigung außer Kraft gesetzt wird). Ein zusätzliches "*" wird im obigen Code angehängt. Sie können einfach auf einen Teil des Dateinamens tippen, und hoffentlich wird das Richtige passieren.
Der Code ist leicht verworren, anstatt set oder assume nullglob
( shopt -s nullglob
) zu verwenden. Wir überprüfen compgen -G
, ob der Globus auf einige Übereinstimmungen erweitert werden kann, erweitern ihn dann sicher zu einem Array und setzen schließlich COMPREPLY, damit das Zitieren robust ist.
Sie können dies teilweise mit bash tun (programmgesteuert ein Glob erweitern) compgen -G
, aber es ist nicht robust, da es ohne Anführungszeichen für stdout ausgibt.
Wie üblich ist die Vervollständigung ziemlich kompliziert, was die Vervollständigung anderer Dinge, einschließlich Umgebungsvariablen, bricht ( Einzelheiten zum Emulieren des Standardverhaltens finden Sie in der _bash_def_completion()
Funktion hier ).
Sie können auch nur compgen
außerhalb einer Vervollständigungsfunktion Folgendes verwenden:
files=( $(compgen -W "$pattern") )
Ein Punkt, den Sie beachten sollten, ist, dass "~" kein Glob ist, sondern von bash in einer separaten Expansionsstufe behandelt wird, ebenso wie $ variables und andere Expansionen. compgen -G
Führt nur das Verschieben von Dateinamen durch, compgen -W
gibt Ihnen jedoch die gesamte Standarderweiterung von bash wieder, obwohl möglicherweise zu viele Erweiterungen (einschließlich ``
und $()
) vorhanden sind. Im Gegensatz zu -G
der -W
ist sicher zitiert (ich die Unterschiede nicht erklären kann). Da der Zweck von darin -W
besteht, dass es Token erweitert, bedeutet dies, dass es "a" zu "a" erweitert, auch wenn keine solche Datei vorhanden ist, so dass es möglicherweise nicht ideal ist.
Dies ist einfacher zu verstehen, kann jedoch unerwünschte Nebenwirkungen haben:
_echo() {
local cur=${COMP_WORDS[COMP_CWORD]}
local files=( $(compgen -W "$cur") )
printf -v COMPREPLY %q "${files[0]}"
}
Dann:
touch $'curious \n filename'
echo curious*
tab
Beachten Sie die Verwendung von printf %q
, um die Werte sicher anzugeben.
Eine letzte Option ist die Verwendung einer durch 0 getrennten Ausgabe mit GNU-Dienstprogrammen (siehe bash FAQ ):
pattern="*.txt"
while IFS= read -r -d $'\0' filename; do
printf '%q' "$filename";
break;
done < <(find . -maxdepth 1 -name "$pattern" -printf "%f\0" | sort -z )
Diese Option gibt Ihnen ein wenig mehr Kontrolle über die Sortierreihenfolge (die Reihenfolge beim Erweitern eines Glob LC_COLLATE
hängt von Ihrem Gebietsschema ab / und kann die Groß- / Kleinschreibung ändern), ist aber ansonsten ein ziemlich großer Hammer für ein so kleines Problem ;-)