Un modo affidabile in bash è di espandersi in un array e produrre solo il primo elemento:
pattern="*.txt"
files=( $pattern )
echo "${files[0]}" # printf is safer!
(Puoi anche solo echo $files
, un indice mancante viene trattato come [0].)
Questo gestisce in modo sicuro spazio / tab / newline e altri metacaratteri durante l'espansione dei nomi dei file. Si noti che le impostazioni locali in effetti possono modificare il "primo".
Puoi anche farlo in modo interattivo con una funzione di completamento 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
Ciò vincola la _echo
funzione per completare gli argomenti al echo
comando (ignorando il completamento normale). Un "*" extra viene aggiunto nel codice sopra, puoi semplicemente premere il tasto tab su un nome file parziale e speriamo che accada la cosa giusta.
Il codice è leggermente contorto, piuttosto che set o assume nullglob
( shopt -s nullglob
) controlliamo di compgen -G
poter espandere il glob in alcune corrispondenze, quindi espandiamo in modo sicuro in un array e infine impostiamo COMPREPLY in modo che il preventivo sia solido.
Puoi parzialmente farlo (espandere a livello di codice un glob) con bash's compgen -G
, ma non è robusto in quanto emette non quotato su stdout.
Come al solito, il completamento è piuttosto irto, questo interrompe il completamento di altre cose, comprese le variabili di ambiente (vedere la _bash_def_completion()
funzione qui per i dettagli sull'emulazione del comportamento predefinito).
Puoi anche usare solo compgen
al di fuori di una funzione di completamento:
files=( $(compgen -W "$pattern") )
Un punto da notare è che "~" non è un glob, è gestito da bash in una fase di espansione separata, così come lo sono le variabili $ e altre espansioni. compgen -G
fa solo il globbing del nome file, ma compgen -W
ti dà tutta l'espansione predefinita di bash, anche se forse troppe espansioni (incluso ``
e $()
). A differenza di -G
, -W
viene citato in modo sicuro (non posso spiegare la disparità). Poiché lo scopo di -W
è che espande i token, ciò significa che espanderà da "a" a "a" anche se non esiste un file simile, quindi forse non è l'ideale.
Questo è più facile da capire, ma può avere effetti collaterali indesiderati:
_echo() {
local cur=${COMP_WORDS[COMP_CWORD]}
local files=( $(compgen -W "$cur") )
printf -v COMPREPLY %q "${files[0]}"
}
Poi:
touch $'curious \n filename'
echo curious*
tab
Nota l'uso di printf %q
per citare in sicurezza i valori.
Un'ultima opzione è usare l'output delimitato da 0 con le utility GNU (vedi le FAQ di 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 )
Questa opzione ti dà un po 'più di controllo sull'ordinamento (l'ordine quando si espande un glob sarà soggetto al tuo locale / LC_COLLATE
e potrebbe o non potrebbe piegare il caso), ma è altrimenti un martello piuttosto grande per un problema così piccolo ;-)