Uma maneira robusta no bash é expandir para uma matriz e gerar apenas o primeiro elemento:
pattern="*.txt"
files=( $pattern )
echo "${files[0]}" # printf is safer!
(Você pode apenas echo $files
, um índice ausente é tratado como [0].)
Isso lida com segurança com espaço / tab / nova linha e outros metacaracteres ao expandir os nomes de 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 do 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 vincula a _echo
função a concluir argumentos para o echo
comando (substituindo a conclusão normal). Um "*" extra é anexado no código acima, você pode simplesmente pressionar a tecla Tab em um nome de arquivo parcial e esperamos que a coisa certa aconteça.
O código é um pouco complicado, em vez de definir ou assumir nullglob
( shopt -s nullglob
) que verificamos compgen -G
pode expandir o glob para algumas correspondências, depois expandimos com segurança para uma matriz e finalmente configuramos COMPREPLY para que a citação seja robusta.
Em parte, você pode fazer isso (expandir programaticamente um globo) com o bash compgen -G
, mas não é robusto, pois é gerado sem aspas para o stdout.
Como de costume, a conclusão é bastante complicada, isso interrompe a conclusão de outras coisas, incluindo variáveis de ambiente (consulte a _bash_def_completion()
função aqui para obter 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 globo, ele é tratado pelo bash em um estágio separado de expansão, assim como as variáveis $ e outras expansões. compgen -G
apenas faz o globbing do nome do arquivo, mas compgen -W
fornece toda a expansão padrão do bash, embora possivelmente muitas expansões (incluindo ``
e $()
). Ao contrário -G
, o -W
é citado com segurança (não sei explicar a disparidade). Como o objetivo -W
é expandir os tokens, isso significa que ele expandirá "a" para "a" mesmo que esse arquivo não exista, portanto, 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*
tab
Observe o uso de printf %q
para citar com segurança os valores.
Uma opção final é usar saída delimitada com 0 com utilitários GNU (consulte as perguntas frequentes do 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 )
Essa opção oferece um pouco mais de controle sobre a ordem de classificação (a ordem ao expandir uma glob estará sujeita à sua localidade / LC_COLLATE
e pode ou não ser dobrada), mas, de outra forma, é um martelo bastante grande para um problema tão pequeno ;-)