Shell como Bash e Zsh expandem o curinga em argumentos, tantos argumentos quanto o padrão:
$ echo *.txt
1.txt 2.txt 3.txt
Mas e se eu quiser apenas retornar a primeira correspondência, e não todas?
$ echo *.txt
1.txt
Não me importo de soluções específicas de shell, mas gostaria de uma solução que funcione com espaço em branco nos nomes de arquivos.
Respostas:
Uma maneira robusta no bash é expandir para uma matriz e gerar apenas o primeiro elemento:
(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 :
Isso vincula a
_echo
função a concluir argumentos para oecho
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 verificamoscompgen -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: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, mascompgen -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:
Então:
touch $'curious \n filename'
echo curious*
tabObserve 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 ):
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 ;-)fonte
No zsh, use o
[1]
qualificador glob . Observe que, embora esse caso especial retorne no máximo uma correspondência, ainda é uma lista e os globs não são expandidos em contextos que esperam uma única palavra como atribuições (atribuições de matriz de lado).No ksh ou no bash, você pode preencher a lista inteira de correspondências em uma matriz e usar o primeiro elemento.
Em qualquer shell, você pode definir os parâmetros posicionais e usar o primeiro.
Isso derruba os parâmetros posicionais. Se você não quiser, pode usar um subshell.
Você também pode usar uma função, que possui seu próprio conjunto de parâmetros posicionais.
fonte
*.txt([1,n])
Experimentar:
Observe que a expansão do nome do arquivo é classificada de acordo com a sequência de intercalação em vigor no código do idioma atual.
fonte
Uma solução simples:
Ou use
printf
se você preferir.fonte
Eu apenas tropecei nessa pergunta antiga enquanto me perguntava a mesma coisa. Eu acabei com isso:
echo $(ls *.txt | head -n1)
Obviamente, você pode substituir
head
portail
e-n1
por qualquer outro número.O exemplo acima não funcionará se você estiver trabalhando com arquivos com novas linhas em seus nomes. Para trabalhar com novas linhas, você pode usar qualquer um destes:
ls -b *.txt | head -n1 | sed -E 's/\\n/\n/g'
(Não funciona no BSD)ls -b *.txt | head -n1 | sed -e 's/\\n/\'$'\n/g'
ls -b *.txt | head -n1 | perl -pe 's/\\n/\n/g'
echo -e "$(ls -b *.txt | head -n1)"
(Funciona com qualquer caractere especial)fonte
Um caso de uso que encontro frequentemente é definir o diretório superior / inferior após a expansão global (por exemplo, um diretório cheio de SDKs com versão ou ferramentas de construção). Nessa situação, geralmente desejarei salvar esse nome de diretório em uma variável para uso em alguns lugares dentro de um script de shell.
Este comando geralmente faz isso por mim:
Isenção de responsabilidade: a expansão Glob não vai classificar suas pastas nem sempre; voce foi avisado. Isso é ótimo se você tiver uma
Dockerfile
única versão de um diretório, mas essa versão dos diretórios possa variar de imagem para imagem.fonte
mkdir "$(echo one; echo two)"
e veja o que quero dizer.tail
?dirname
usa apenas um único nome de caminho, portanto você não pode confiar nele trabalhando em vários nomes de caminho, a menos que saiba que sua implementação específica é compatível.