Esse é o truque: o comando não sabe, é o shell que faz o trabalho
Considere, por exemplo, grep 'abc' *.txt
. Se executarmos rastros de chamadas do sistema, você verá algo assim:
bash-4.3$ strace -e trace=execve grep "abc" *.txt > /dev/null
execve("/bin/grep", ["grep", "abc", "ADDA_converters.txt", "after.txt", "altera_license.txt", "altera.txt", "ANALOG_DIGITAL_NOTES.txt", "androiddev.txt", "answer2.txt", "answer.txt", "ANSWER.txt", "ascii.txt", "askubuntu-profile.txt", "AskUbuntu_Translators.txt", "a.txt", "bash_result.txt", ...], [/* 80 vars */]) = 0
+++ exited with 0 +++
O shell expandiu *.txt
em todos os nomes de arquivos no diretório atual que terminam com .txt
extension. Então, efetivamente, seu shell traduz o comando grep 'abc' *.txt
para grep 'abc' file1.txt file2.txt file3.txt . . .
. Assim, sua segunda suposição está correta.
*
como argumento string ao comando, mas é o trabalho do comando decidir o que fazer com ele. A expansão do nome de arquivo, no entanto, é propriedade da sua respectiva shell, como já mencionei.
No entanto, isso só funciona porque o grep aceita vários arquivos no final. É possível que outro comando permita apenas que 1 arquivo seja passado.
Exatamente certo! Os programas não limitam o número de argumentos de linha de comando aceitáveis (por exemplo, em C, é o array de strings const char *args[]
e em python sys.argv[]
), mas eles podem detectar o comprimento desse array ou se algo inesperado está ou não na posição errada da matriz. grep
não faz isso e aceita vários arquivos, o que é por design.
Em nota lateral, citações impróprias acopladas a globbing com grep às vezes podem ser um problema. Considere isto:
bash-4.3$ echo "one two" | strace -e trace=execve grep *est*
execve("/bin/grep", ["grep", "self_test.sh", "test.wxg"], [/* 80 vars */]) = 0
+++ exited with 1 +++
O usuário despreparado esperaria que o grep correspondesse a qualquer linha com est
letras vindas do pipe, mas, em vez disso, a expansão do nome de arquivo do shell era distorcida. Eu vi isso acontecer muito com pessoas que fazem ps aux | grep shell_script_name.sh
, e eles esperam encontrar o processo em execução, mas porque eles executaram o comando do mesmo diretório onde o script era , a expansão do nome de arquivo do shell tornou grep
comando para parecer completamente diferente nos bastidores do que o usuário esperava.
A maneira correta seria usar aspas simples:
bash-4.3$ echo "one two" | strace -e trace=execve grep '*est*'
execve("/bin/grep", ["grep", "*est*"], [/* 80 vars */]) = 0
+++ exited with 1 +++