Como um shell (bash, por exemplo) expande padrões de curingas?

8

Suponha que um diretório tenha 100 arquivos começando com a letra 'a'.

Se eu fizer um grep <some string> a* do terminal, como o shell lidará com isso?

Expandirá a expressão regular, obterá uma lista de todos os arquivos que começam com a e grep em cada um deles sequencialmente? Ou existe alguma outra maneira?

Suponha que eu tenha uma matriz dos nomes de arquivos acima que começam com 'a'. Será que vai demorar mais / menos tempo se eu escrever um loop for e fazer a iteração sozinho em um script de shell ou um programa c?

    
por harithski 03.08.2011 / 08:53

2 respostas

8

Primeiro, um nitpick: uma string como a* na sintaxe normal da shell é uma glob, que funciona de maneira diferente das expressões regulares.

Em uma visão geral de alto nível, o interpretador de shell (ou seja, o bash) expande a string a* para uma lista de todos os nomes de arquivos correspondentes ao padrão a* . Estes então se tornam parte dos parâmetros da linha de comando para uma instância single de grep (para os programadores, todas as palavras expandidas vão como strings separadas no argumento argv de main ). Esse comando grep único analisa os argumentos da maneira que escolher, e cabe a grep interpretar esses argumentos como nomes de arquivos, opções, argumentos de opção, expressões regulares, etc. e tomar as ações apropriadas. Tudo ocorre seqüencialmente (AFAIK no grep implementation usa vários threads).

Se você implementar um loop em um shell script para fazer a mesma coisa, é quase garantido que seja mais lento que o processo acima, pelos seguintes motivos. Se você criar um novo processo de grep para cada arquivo, ele certamente será mais lento devido à sobrecarga de criação do processo sendo multiplicada desnecessariamente. Se você construiu a lista de argumentos no shell script e usou uma única instância de grep , qualquer coisa que você fizer no shell ainda será mais lenta porque os comandos do shell devem ser interpretados (pelo bash), que adiciona uma camada extra de código e você estará apenas reimplementando o que o bash já fazia internamente mais rápido no código compilado.

Quanto a escrever você mesmo em C, você provavelmente pode facilmente obter desempenho comparável ao processo descrito no primeiro parágrafo, mas é improvável que você consiga obter um ganho de desempenho suficiente sobre as implementações atuais do grep / bash para justifique o tempo gasto sem se aprofundar nas otimizações de desempenho específicas da máquina ou sacrificar a portabilidade. Talvez você possa tentar criar uma versão arbitrariamente parallelizable de grep , mas mesmo isso pode não ajudar, já que é mais provável que você esteja limitado a I / O do que a CPU. A expansão glob e o grep já são "rápidos o suficiente" para a maioria das finalidades "normais".

    
por 03.08.2011 / 10:42
6

Sim, ele será expandido para uma lista de arquivos e alimentará a lista resultante para o programa grep . Pelo menos é isso que man bash diz na subseção Expansão do nome do caminho .

Existe uma outra maneira de usar a expansão em casos simples, como você menciona: escreva grep <some_string> a e antes de pressionar * , pressione ESC . Isso expandirá a lista de arquivos correspondentes diretamente na linha de comando, para que você possa verificar se a lista está OK antes de pressionar Enter .

Quanto à segunda parte da sua pergunta, isso depende. Se você quer escrever um loop para executar o grep em cada um dos arquivos, então seria definitivamente mais lento, porque o programa grep será executado não uma vez, mas uma vez por arquivo. No entanto, o que é importante ter em mente é que existe um certo limite no comprimento expandido dos argumentos de linha de comando que você pode usar, embora seja tipicamente bastante alto. Para ver isso, você pode tentar grep adasdsadf /usr/*/*/* >/dev/null .

    
por 03.08.2011 / 10:09