Selon ma compréhension, un caractère générique glob est interprété par le shell, qui exécute ensuite la commande donnée pour chaque nom de fichier correspondant. Supposons que j'ai des fichiers :abc1, abc2, and abc3
dans mon répertoire actuel. Ensuite, par exemple, echo abc*
fera écho une fois pour chaque nom de fichier commençant par "abc".
Cependant, si je lance grep 'foo' abc*
, j'imagine que cela devrait fonctionner :
grep 'foo' abc1
grep 'foo' abc2
grep 'foo' abc3
Ce qui signifie que je devrais obtenir le résultat suivant (en supposant que tous les fichiers contiennent une ligne indiquant "foo") :
foo
foo
foo
Cependant, à la place, j'obtiens :
abc1:foo
abc2:foo
abc3:foo
Je pense donc qu'il y a 2 explications possibles à cela. Tout d'abord, grep peut en quelque sorte détecter qu'il a été utilisé avec une expression glob et répond en affichant les noms de fichiers avant les correspondances. Deuxièmement, puisque vous pouvez transmettre plusieurs fichiers à grep, le shell n'exécute en réalité qu'une seule commande :
grep 'foo' abc1 abc2 abc3
Cependant, cela ne fonctionne que parce que grep accepte plusieurs fichiers à la fin. Il est possible qu'une autre commande n'autorise qu'un seul fichier à transmettre. Donc, si vous vouliez exécuter la commande pour plusieurs fichiers correspondant au glob, cela ne fonctionnerait pas si le globbing fonctionnait via la deuxième méthode décrite ci-dessus.
Quoi qu'il en soit, quelqu'un peut-il nous éclairer là-dessus ?
Merci !
Réponse acceptée :
C'est le truc :la commande ne sait pas, c'est le shell qui fait le travail
Considérons par exemple grep 'abc' *.txt
. Si nous exécutons la trace des appels système, vous verrez quelque chose comme ceci :
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 +++
Le shell a développé *.txt
dans tous les noms de fichiers du répertoire courant qui se terminent par .txt
extension. Donc, efficacement, votre shell traduit le grep 'abc' *.txt
commande dans grep 'abc' file1.txt file2.txt file3.txt . . .
. Ainsi, votre deuxième hypothèse est correcte.
La première hypothèse n'est pas correcte - les programmes n'ont aucun moyen de détecter glob. Il est possible de passer *
comme argument de chaîne à la commande, mais c'est le travail de la commande de décider quoi en faire ensuite. L'expansion du nom de fichier, cependant, est la propriété de votre shell respectif, comme je l'ai déjà mentionné.
Cependant, cela ne fonctionne que parce que grep accepte plusieurs fichiers à la fin. Il est possible qu'une autre commande n'autorise qu'un seul fichier à transmettre.
Exactement exact ! Les programmes ne limitent pas le nombre d'arguments de ligne de commande acceptables (par exemple, en C, c'est un tableau de chaînes const char *args[]
et en python sys.argv[]
), mais ils peuvent détecter la longueur de ce tableau ou si quelque chose d'inattendu est dans la mauvaise position du tableau. grep
ne le fait pas et accepte plusieurs fichiers, ce qui est voulu par sa conception.
En passant, des citations incorrectes couplées à un globbing avec grep peuvent parfois être un problème. Considérez ceci :
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 +++
Un utilisateur non préparé s'attendrait à ce que grep corresponde à n'importe quelle ligne avec est
lettres qu'il contient provenant du tuyau, mais à la place, l'expansion du nom de fichier du shell a tout tordu. J'ai souvent vu cela arriver avec des gens qui font ps aux | grep shell_script_name.sh
, et ils s'attendent à trouver leur processus en cours d'exécution, mais parce qu'ils ont exécuté la commande à partir du même répertoire où le script était , l'expansion du nom de fichier du shell a fait grep
commande pour avoir un aspect complètement différent dans les coulisses de ce à quoi l'utilisateur s'attendait.
La bonne façon serait d'utiliser des guillemets simples :
bash-4.3$ echo "one two" | strace -e trace=execve grep '*est*'
execve("/bin/grep", ["grep", "*est*"], [/* 80 vars */]) = 0
+++ exited with 1 +++