Dans quel ordre les commands par piping s'exécutent-elles?

Je n'ai jamais vraiment pensé à la façon dont le shell exécute réellement les commands pipées. On m'a toujours dit que le «stdout d'un programme est envoyé dans le stdin d'un autre», comme une façon de penser à des tuyaux. Alors naturellement, je pensais que dans le cas de dire, A | B, A s'exécuterait d'abord, puis B recevrait la sortie standard de A et utiliserait la sortie standard de A comme input.

Mais j'ai remarqué que lorsque les gens searchnt un process particulier dans ps, ils incluraient grep -v "grep" à la fin de la command pour s'assurer que grep n'apparaît pas dans la sortie finale. Cela signifie que dans la command ps aux | grep "bash" | grep -v "grep", ce qui signifie que ps savait que grep était en cours d'exécution et est donc dans la sortie de ps. Mais si ps finit de s'exécuter avant que sa sortie ne devienne grep, comment a-t-il su que grep était en cours d'exécution?

flamingtoast@FTOAST-UBUNTU: ~$ ps | grep ".*" PID TTY TIME CMD 3773 pts/0 00:00:00 bash 3784 pts/0 00:00:00 ps 3785 pts/0 00:00:00 grep 

Les commands pipées s'exécutent simultanément. Lorsque vous exécutez ps | grep … ps | grep … , c'est la chance du tirage (ou une question de détails du fonctionnement de la coquille combinée avec le programmateur affinant profondément dans les entrailles du kernel) pour savoir si ps ou grep commence en premier et, en tout cas, ils continuent pour s'exécuter en même time.

Ceci est très communément utilisé pour permettre au deuxième programme de traiter datatables telles qu'elles sortent du premier programme, avant que le premier programme ait terminé son opération. Par exemple

 grep pattern very-large-file | tr az AZ 

commence à afficher les lignes correspondantes en majuscules avant même que grep n'ait fini de parcourir le file volumineux.

 grep pattern very-large-file | head -n 1 

affiche la première ligne correspondante et peut arrêter le traitement bien avant que grep ait fini de lire son file d'input.

Si vous lisez quelque part que les programmes en cascade s'exécutent en séquence, fuyez ce document. Les programmes pipés fonctionnent simultanément et ont toujours.

L'ordre d'exécution des commands n'a pas d'importance et n'est pas garanti. En laissant de côté les détails arcanes de pipe() , fork() , dup() et execve() , le shell crée d'abord le tube, le conduit pour datatables qui vont s'écouler entre les process, puis crée les process avec les extrémités de le tuyau qui leur est connecté. Le premier process exécuté peut bloquer l'attente de l'input du deuxième process ou bloquer l'attente du deuxième process pour commencer à lire datatables du canal. Ces attentes peuvent être arbitrairement longues et ne countnt pas. Quel que soit l'ordre dans lequel les process sont exécutés, datatables sont finalement transférées et tout fonctionne.

Au risque de battre un cheval mort, l'idée fausse semble être que

  A |  B 

est équivalent à

     A > file_temporaire
     B < file_temporaire
     rm temporary_file

Mais à l'époque où Unix était créé et où les enfants montaient les dinosaures à l'école, les disques étaient très petits, et il était fréquent qu'une command plutôt bénigne consum tout l'espace libre dans un système de files. Si B était quelque chose comme grep some_very_obscure_ssortingng , la sortie finale du pipeline pourrait être beaucoup plus petite que ce file intermédiaire. Par conséquent, le pipe a été développé, pas comme un raccourci pour le "exécuter un premier, puis exécuter B avec input du model de sortie de A", mais comme un moyen pour B d'exécuter simultanément avec A et éliminer la nécessité de stocker le file intermédiaire sur le disque.

Typiquement, vous exécutez cela sous bash. process de travail et de démarrage en même time, mais sont en cours d'exécution par le shell en parallèle. Comment est-ce possible?

  1. si ce n'est pas la dernière command dans le tuyau, créez un tuyau sans nom avec une paire de sockets
  2. fourchette
  3. dans l'enfant réaffecter stdin / stdout aux sockets si c'est nécessaire (pour le premier process dans pipe stdin n'est pas réassigné, le même pour le dernier process et sa sortie standard)
  4. dans la command EXEC spécifiée par l'enfant avec des arguments qui balaient le code shell d'origine, mais laisse tous ouverts par les sockets. l'ID du process enfant ne sera pas modifié car il s'agit du même process enfant
  5. concurremment avec l'enfant mais parallèlement sous la shell principale, passez à l'étape 1.

Le système ne garantit pas la rapidité avec laquelle l'exécution sera exécutée et la command spécifiée démarre. c'est indépendant de la coquille, mais du système. Ceci est dû au fait:

 ps auxww| grep ps | cat 

affichez une fois la command grep et / ou ps , puis la suite. Cela dépend de la rapidité avec laquelle le kernel démarre réellement les process en utilisant la fonction exec du système.