Uma maneira robusta no bash é expandir em uma matriz e produzir apenas o primeiro elemento:
pattern="*.txt"
files=( $pattern )
echo "${files[0]}" # printf is safer!
(Você pode até mesmo echo $files
, um índice ausente é tratado como [0].)
Isso lida com segurança com espaço / tabulação / nova linha e outros metacaracteres ao expandir os nomes dos arquivos. Observe que as configurações de localidade em vigor podem alterar o que "primeiro" é.
Você também pode fazer isso interativamente com uma função de conclusão 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
Isso liga a função _echo
para concluir os argumentos para o comando echo
(substituindo a conclusão normal). Um extra "*" é acrescentado no código acima, você pode simplesmente clicar em um nome parcial do arquivo e esperamos que aconteça a coisa certa.
O código é ligeiramente complicado, em vez de definir ou assumir nullglob
( shopt -s nullglob
) verificamos compgen -G
podemos expandir a glob para algumas correspondências, depois expandimos com segurança para uma matriz, e finalmente definir COMPRELIR para que a cotação seja robusta.
Você pode parcialmente fazer isso (expandir programaticamente um glob) com o compgen -G
do bash, mas ele não é robusto, pois gera resultados não referenciados para o stdout.
Como de costume, a conclusão é bastante complicada, isso quebra a conclusão de outras coisas, incluindo variáveis de ambiente (consulte a função _bash_def_completion()
aqui para detalhes sobre como emular o comportamento padrão).
Você também pode usar apenas compgen
fora de uma função de conclusão:
files=( $(compgen -W "$pattern") )
Um ponto a ser observado é que "~" não é um glob, é manipulado por bash em um estágio separado de expansão, assim como variáveis $ e outras expansões. compgen -G
apenas faz a globalização de nome de arquivo, mas compgen -W
fornece toda a expansão padrão do bash, embora possivelmente muitas expansões (incluindo ''
e $()
). Ao contrário de -G
, o -W
é citado com segurança (não consigo explicar a disparidade). Como o propósito de -W
é que ele expande os tokens, isso significa que ele expandirá "a" para "a" mesmo que não exista esse arquivo, por isso talvez não seja o ideal.
Isso é mais fácil de entender, mas pode ter efeitos colaterais indesejados:
_echo() {
local cur=${COMP_WORDS[COMP_CWORD]}
local files=( $(compgen -W "$cur") )
printf -v COMPREPLY %q "${files[0]}"
}
Então:
touch $'curious \n filename'
echo curious*
guia
Observe o uso de printf %q
para citar com segurança os valores.
Uma opção final é usar a saída delimitada por 0 com utilitários GNU (consulte o FAQ do bash ):
pattern="*.txt"
while IFS= read -r -d $'pattern="*.txt"
files=( $pattern )
echo "${files[0]}" # printf is safer!
' filename; do
printf '%q' "$filename";
break;
done < <(find . -maxdepth 1 -name "$pattern" -printf "%f_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
" | sort -z )
Esta opção lhe dá um pouco mais de controle sobre a ordem de classificação (a ordem de expansão de glob estará sujeita a sua localidade / LC_COLLATE
e pode ou não dobrar maiúsculas), mas é um martelo bastante grande para tal um pequeno problema; -)