Pode parecer que prefix-*
deve ser fácil de transformar, por exemplo, prefix-1 prefix-2
, pois estamos acostumados a ver as listagens de diretório classificadas. Mas acontece que muito poucos sistemas de arquivos podem realmente produzir listagens de nomes de arquivos classificadas e, além disso, não existe uma API padrão para solicitar listagens de nomes de arquivos classificadas.
Se um programa - como ls
ou bash
- precisar de uma lista de nomes de arquivos, ele precisará ler toda a lista de diretórios, que será produzida em uma ordem aleatória (geralmente a ordem está relacionada ao tempo de criação, às vezes é baseada em um hash do nome do arquivo, mas em muito bom caso, não é uma simples ordem alfabética). Portanto, para resolver prefix-*
, você precisa ler o diretório inteiro e verificar cada nome de arquivo em relação ao padrão. Como a parte mais cara desse procedimento é a leitura do diretório, faz pouca diferença a complexidade do padrão ou quantos nomes de arquivo correspondem ao padrão.
Em resumo, a expansão do nome do caminho ("resolving globs") será lenta em um diretório grande. Essa é uma razão para evitar diretórios grandes, em vez de um motivo para evitar globs.
Mas há outro ponto de dados importante: prefix-{1,2}
é não expansão do nome do caminho. É " expansão de contraventamentos " e é uma extensão do padrão shell Posix (embora quase todos os shells implementam isso). Há um número de diferenças entre expansão de chave e expansão de nome de caminho, mas uma diferença importante e relevante é que a expansão de brace não depende da existência de arquivos . A expansão do suporte é uma operação de string simples.
Consequentemente, prefix-{1,2}
será sempre expandido para prefix-1 prefix-2
, independentemente de esses arquivos existirem ou não. Isso significa que ele pode ser expandido sem ler o diretório e sem stat
em qualquer arquivo. Claramente, isso será rápido. Mas há uma desvantagem: não há como saber se o resultado corresponde a arquivos reais.
Considere o seguinte exemplo simples:
$ mkdir test && cd test
$ touch file1 file2 file4
$ ls file*
file1 file2 file4
$ ls file[1234]
file1 file2 file4
$ ls file{1,2,3,4}
ls: cannot access file3: No such file or directory
file1 file2 file4
Ponto final: A expansão do nome do caminho é feita pelo shell, não por ls
. Com a expansão do nome do caminho, poderíamos usar também echo
:
$ echo file*
file1 file2 file4
$ echo file[1234]
file1 file2 file4
E echo
produzirá a lista um pouco mais rápida, porque todo echo
precisa fazer é imprimir seus argumentos, enquanto ls
(que recebe os mesmos argumentos) tem que stat
cada argumento para verificar se é um arquivo. Esse stat
- que não é uma chamada barata - é totalmente redundante no caso de uma expansão de nome de caminho, porque o shell já usou a lista de diretórios para filtrar a lista de arquivos e, portanto, cada nome de arquivo foi passado para ls
é conhecido por existir. (A menos que o glob não corresponda a nenhum arquivo).
Além disso, echo é um bash
interno, portanto, pode ser chamado sem criar um processo filho.
No caso da expansão de chaves, no entanto, echo
não produz o mesmo resultado:
$ echo file{1,2,3,4}
file1 file2 file3 file4
Portanto, poderíamos usar ls
, redirecionando sua saída de erro para o intervalo de bits:
$ ls file{1,2,3,4}
file1 file2 file4
e, neste caso, as chamadas stat
não são redundantes porque o shell nunca validou os nomes dos arquivos.
A menos que seus diretórios sejam realmente grandes, nada disso fará muita diferença e o glob será muito mais fácil de escrever. Se os seus diretórios são realmente enormes, você deve considerar dividi-los em subdiretórios menores.
Por exemplo, em vez de caminhos como:
/var/log/remote/serverX.domain.local/ps/ps2.log.2014-mm-dd.gz
você pode usar:
/var/log/remote/serverX/domain.local/ps/ps2.log.2014-mm-dd-gz
E se você estiver mantendo os registros para sempre, talvez queira extrair o ano para evitar o aumento infinito do tamanho do diretório:
/var/log/remote/2014/serverX/domain.local/ps/ps2.log.2014-mm-dd-gz
( 2014
é deliberadamente repetido).
A divisão dos diretórios geralmente será uma grande vitória, pois fornece um mecanismo para otimizar a globalização. Como mencionado acima, o shell não pode otimizar
/var/log/remote/server[2357].domain.local/ps/ps2.log.2014-10-*-gz
mas pode otimizar
/var/log/remote/server[2357]/domain.local/ps/ps2.log.2014-10-*-gz
No segundo caso, server[2357]
precisa ser correspondido apenas aos nomes dos diretórios e, uma vez feito isso, ps2.log.2014-10-*-gz
só precisa ser comparado aos nomes dos arquivos nos diretórios correspondentes.