Una forma sólida en bash es expandirse en una matriz y generar solo el primer elemento:
pattern="*.txt"
files=( $pattern )
echo "${files[0]}" # printf is safer!
(Incluso puede simplemente echo $files
, un índice faltante se trata como [0]).
Esto maneja de forma segura el espacio / tabulador / nueva línea y otros metacaracteres al expandir los nombres de archivo. Tenga en cuenta que la configuración regional vigente puede alterar lo que es "primero".
También puede hacer esto interactivamente con una función de finalización 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
Esto vincula la _echo
función para completar argumentos al echo
comando (anulando la finalización normal). Se agrega un "*" adicional en el código anterior, solo puede presionar la pestaña en un nombre de archivo parcial y, con suerte, sucederá lo correcto.
El código es un poco complicado, en lugar de establecer o asumir nullglob
( shopt -s nullglob
) comprobamos que compgen -G
puede expandir el glob a algunas coincidencias, luego expandimos de forma segura en una matriz y finalmente establecemos COMPREPLY para que las citas sean robustas.
Puede parte hacer esto (mediante programación expandir un pegote) con fiesta de compgen -G
, pero no es tan robusta que da salida a la salida estándar sin comillas.
Como de costumbre, la finalización es bastante complicada, esto interrumpe la finalización de otras cosas, incluidas las variables de entorno (consulte la _bash_def_completion()
función aquí para obtener detalles sobre cómo emular el comportamiento predeterminado).
También podría usarlo compgen
fuera de una función de finalización:
files=( $(compgen -W "$pattern") )
Un punto a tener en cuenta es que "~" no es un problema, se maneja con bash en una etapa separada de expansión, al igual que las variables $ y otras expansiones. compgen -G
solo hace globing de nombre de archivo, pero compgen -W
le brinda toda la expansión predeterminada de bash, aunque posiblemente demasiadas expansiones (incluyendo ``
y $()
). A diferencia -G
, -W
se cita con seguridad (no puedo explicar la disparidad). Dado que el propósito -W
es que expande los tokens, esto significa que expandirá "a" a "a" incluso si no existe tal archivo, por lo que tal vez no sea ideal.
Esto es más fácil de entender, pero puede tener efectos secundarios no deseados:
_echo() {
local cur=${COMP_WORDS[COMP_CWORD]}
local files=( $(compgen -W "$cur") )
printf -v COMPREPLY %q "${files[0]}"
}
Luego:
touch $'curious \n filename'
echo curious*
tab
Tenga en cuenta el uso de printf %q
para citar de forma segura los valores.
Una última opción es usar una salida delimitada por 0 con utilidades GNU (consulte las preguntas frecuentes 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 )
Esta opción le da un poco más de control sobre el orden de clasificación (el orden al expandir un globo estará sujeto a su ubicación / LC_COLLATE
y puede o no doblar mayúsculas y minúsculas), pero de lo contrario es un martillo bastante grande para un problema tan pequeño ;-)