Ubuntu – Comment une commande (c’est-à-dire grep) sait-elle quand elle est exécutée dans le cadre de l’expansion glob

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 cours 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 la sortie suivante (en supposant que tous les fichiers contiennent une ligne qui dit ‘foo’):

 foo
foo
foo
 

Cependant, je reçois à la place:

 abc1:foo
abc2:foo
abc3:foo
 

Je pense donc qu’il y a 2 explications possibles à cela. Premièrement, grep peut en quelque sorte détecter qu’il a été utilisé avec des expressions globales et répond en sortant les noms de fichiers avant les correspondances. Deuxièmement, puisque vous pouvez passer plusieurs fichiers à grep, le shell n’exécute en fait 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 ne permette qu’un seul fichier à passer. 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 éclairer cela?

Merci!

Meilleure réponse

  • Voilà l’astuce:la commande ne sait pas, c’est le shell qui fait le boulot

    Considérez 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 +++
     

    La coquille s’est développée *.txt dans tous les noms de fichiers du répertoire courant qui se terminent par .txt extension. Si efficacement, votre coque traduit la grep 'abc' *.txt commande en 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 à commander, 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 ne permette qu’un seul fichier à passer.

    Exactement! 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étecterlongueurde ce tableau ou si quelque chose d’inattendu est ou non dans la mauvaise position du tableau. grep ne le fait pas et accepte plusieurs fichiers, ce qui est voulu par la conception même du produit.


    Sur le côté, une citation incorrecte associée à un globbing avec grep peut parfois être un problème. Considère 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 les lettres provenant du tuyau, mais à la place, l’expansion du nom de fichier shell tord tout autour. J’ai vu cela se produire souvent avec des gens 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 demême répertoire où le script était, extension du nom de fichier du shell faite grep commande de regarder complètement différent dans les coulisses de ce que l’utilisateur 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 +++