Une méthode efficace dans bash consiste à développer un tableau et à ne générer que le premier élément:
pattern="*.txt"
files=( $pattern )
echo "${files[0]}" # printf is safer!
(Vous pouvez même simplement echo $files
, un index manquant est traité comme [0].)
Ceci gère en toute sécurité les espaces / tab / newline et autres métacaractères lors du développement des noms de fichiers. Notez que les paramètres régionaux en vigueur peuvent modifier le "premier".
Vous pouvez également le faire de manière interactive avec une fonction d'achèvement de bash :
_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
Cela lie la _echo
fonction pour compléter les arguments de la echo
commande (écrasant l'achèvement normal). Un "*" supplémentaire est ajouté dans le code ci-dessus, vous pouvez simplement appuyer sur tab sur un nom de fichier partiel et j'espère que la bonne chose se passera.
Le code est légèrement compliqué, plutôt que de définir ou de supposer nullglob
( shopt -s nullglob
) nous vérifions que nous compgen -G
pouvons étendre le glob à quelques correspondances, puis nous développons en toute sécurité dans un tableau, et définissons enfin COMPREPLY pour que la citation soit robuste.
Vous pouvez faire ceci en partie (développer un glob par programme) avec les bash compgen -G
, mais ce n'est pas robuste car il sort en sortie de stdout.
Comme d'habitude, l'achèvement est plutôt complexe, cela rompt l'achèvement d'autres choses, y compris les variables d'environnement (voir la _bash_def_completion()
fonction ici pour plus de détails sur l'émulation du comportement par défaut).
Vous pouvez également simplement utiliser en compgen
dehors d'une fonction d'achèvement:
files=( $(compgen -W "$pattern") )
Un point à noter est que "~" n’est pas un glob, il est traité par bash dans une phase d’expansion distincte, comme le sont $ variables et d’autres expansions. compgen -G
ne fait que balayer le nom de fichier, mais compgen -W
vous donne toute l’extension par défaut de bash, bien qu’elle soit peut-être trop étendue (y compris ``
et $()
). Contrairement à -G
, le -W
est cité en toute sécurité (je ne peux pas expliquer la disparité). Comme le but de -W
est de développer les jetons, cela signifie qu'il étendra "un" en "même" même si aucun fichier de ce type n'existe, ce n'est donc peut-être pas idéal.
Ceci est plus facile à comprendre, mais peut avoir des effets secondaires indésirables:
_echo() {
local cur=${COMP_WORDS[COMP_CWORD]}
local files=( $(compgen -W "$cur") )
printf -v COMPREPLY %q "${files[0]}"
}
Ensuite:
touch $'curious \n filename'
echo curious*
tab
Notez l'utilisation de printf %q
pour citer en toute sécurité les valeurs.
Une dernière option consiste à utiliser une sortie délimitée par 0 avec les utilitaires GNU (voir la FAQ de bash ):
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 )
Cette option vous donne un peu plus de contrôle sur l’ordre de tri (l’ordre lors de l’extension d’un glob est sujet à votre locale / LC_COLLATE
et peut se plier ou non à la casse), mais c’est plutôt un gros marteau pour un problème aussi petit ;-)