Je n'ai pas beaucoup d'expérience dans l'utilisation du tee, donc j'espère que ce n'est pas très basique.
Après avoir consulté l'une des réponses à cette question, je suis tombé sur un comportement étrange tee
.
Pour que je puisse sortir la première ligne et une ligne trouvée, je peux utiliser ceci:
ps aux | tee >(head -n1) | grep syslog
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
syslog 806 0.0 0.0 34600 824 ? Sl Sep07 0:00 rsyslogd -c4
Cependant, la première fois que j'ai exécuté ceci (en zsh), le résultat était dans le mauvais ordre, les en-têtes de colonne étaient en dessous des résultats grep (cela ne s'est toutefois pas reproduit), j'ai donc essayé de permuter les commandes:
ps aux | tee >(grep syslog) | head -n1
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
Seule la première ligne est imprimée, et rien d'autre! Puis-je utiliser tee pour rediriger vers grep, ou est-ce que je fais cela de manière incorrecte?
Comme je tapais cette question, la deuxième commande a réellement fonctionné une fois pour moi, je l'ai exécutée à nouveau cinq fois, puis de nouveau au résultat d'une ligne. Est-ce juste mon système? (J'utilise zsh dans tmux).
Enfin, pourquoi avec la première commande "grep syslog" n'est-il pas affiché comme résultat (il n'y a qu'un seul résultat)?
Pour le contrôle, voici le grep sans le tee
ps aux | grep syslog
syslog 806 0.0 0.0 34600 824 ? Sl Sep07 0:00 rsyslogd -c4
henry 2290 0.0 0.1 95220 3092 ? Ssl Sep07 3:12 /usr/bin/pulseaudio --start --log-target=syslog
henry 15924 0.0 0.0 3128 824 pts/4 S+ 13:44 0:00 grep syslog
Mise à jour: Il semble que head provoque la troncature de toute la commande (comme indiqué dans la réponse ci-dessous), la commande ci-dessous renvoie maintenant ce qui suit:
ps aux | tee >(grep syslog) | head -n1
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
syslog 806
ps aux | sed -n -e '1p' -e '/syslog/p'
.Réponses:
Les commandes
grep
ethead
commencent à peu près au même moment et reçoivent toutes les deux les mêmes données d'entrée à leur guise, mais généralement lorsque les données deviennent disponibles. Il y a certaines choses qui peuvent introduire la sortie «non synchronisée» qui retourne les lignes; par exemple:Les données multiplexées de sont
tee
effectivement envoyées à un processus avant l'autre, en fonction principalement de la mise en œuvre detee
. Unetee
implémentation simple aura uneread
certaine quantité d'entrée, puiswrite
deux fois: une fois pour stdout et une fois pour son argument. Cela signifie que l'une de ces destinations obtiendra les données en premier.Cependant, les tuyaux sont tous tamponnés. Il est probable que ces tampons soient d'une ligne chacun, mais ils peuvent être plus grands, ce qui peut amener l'une des commandes de réception à voir tout ce dont elle a besoin pour la sortie (c'est-à-dire la
grep
ligne ped) avant que l'autre commande (head
) n'ait reçu de données à tout.Nonobstant ce qui précède, il est également possible que l'une de ces commandes reçoive les données mais ne puisse rien faire avec elles à temps, et que l'autre commande reçoive plus de données et les traite rapidement.
Par exemple, même si
head
etgrep
sont envoyées les données une ligne à la fois, si elleshead
ne savent pas comment les traiter (ou sont retardées par la planification du noyau), ellesgrep
peuvent afficher leurs résultats avanthead
même d'avoir la chance de le faire. Pour démontrer, essayez d'ajouter un délai:ps aux | tee >(sleep 1; head -n1) | grep syslog
cela produira presque certainement lagrep
sortie en premier.Je crois que vous obtenez souvent une seule ligne ici, car
head
reçoit la première ligne d'entrée, puis ferme son stdin et quitte. Quandtee
voit que sa sortie standard a été fermée, il ferme alors sa propre sortie standardps
et sort. Cela pourrait dépendre de la mise en œuvre.En effet, les seules données qui
ps
arrive à envoyer est la première ligne ( sans aucun doute, carhead
on contrôle cela), et peut - être quelques autres lignes avanthead
ettee
fermer les descripteurs stdin.L'incohérence avec l'apparition de la seconde ligne est introduite par le timing:
head
ferme stdin, maisps
envoie toujours des données. Ces deux événements ne sont pas bien synchronisés, donc la ligne contenant asyslog
encore une chance de passer àtee
l'argument (lagrep
commande). Ceci est similaire aux explications ci-dessus.Vous pouvez éviter complètement ce problème en utilisant des commandes qui attendent toutes les entrées avant de fermer stdin / quitter. Par exemple, utilisez
awk
au lieu dehead
, qui lira et traitera toutes ses lignes (même si elles ne provoquent aucune sortie):Mais notez que les lignes peuvent toujours apparaître dans le désordre, comme ci-dessus, ce qui peut être démontré par:
J'espère que ce n'était pas trop de détails, mais il y a beaucoup de choses simultanées qui interagissent les unes avec les autres. Des processus distincts s'exécutent simultanément sans aucune synchronisation, de sorte que leurs actions sur une exécution particulière peuvent varier; Parfois, cela aide à approfondir les processus sous-jacents pour expliquer pourquoi.
la source
ps aux | tee >(grep syslog) | head -n1
qui arrêterait dehead
fermer stdout. Wow, cette commande a commencé à donner une sortie maintenant, mais comme cela se produirait conformément à votre réponse, elle semble être tronquéeUSER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
syslog 806
head
. J'ai mis à jour la réponse avec cet exemple:ps aux | tee >(grep syslog) | awk 'NR == 1'
>(cmd)
, le shell crée un canal nommé et le transmet comme argument à la commande (tee
). Puistee
écrit sur stdout (canalisé versawk
) et aussi sur cet argument. C'est la même chose quemkfifo a_fifo ; grep ... a_fifo
dans un shell etps | tee a_fifo | awk ...
dans un autre.echo >(exit 0)
, qui fera écho à l'argument réel passé par le shell (dans mon cas, il devient/dev/fd/63
). Cela devrait fonctionner de la même manière sur bash et zsh.grep syslog
n'est pas toujours affiché car cela dépend du timing. Lorsque vous utilisez un pipeline shell, vous exécutez des commandes presque simultanément. Mais l'élément clé ici est le mot "presque". Sips
termine l'analyse de tous les processus avant le lancement de grep, il ne sera pas sur la liste. Vous pouvez obtenir des résultats aléatoires en fonction de la charge du système, etc.La même chose se produit avec votre tee-shirt. Il est exécuté en arrière-plan en sous-shell et il peut être déclenché avant ou après grep. C'est pourquoi l'ordre de sortie est incohérent.
Quant à la question tee, son comportement est assez étrange. C'est parce qu'il n'est pas utilisé de façon normale. Il est exécuté sans aucun argument, ce qui signifie qu'il doit simplement copier les données de son stdin vers stdout. Mais sa sortie standard est redirigée vers le sous-shell exécutant head (dans le premier cas) ou grep (2e cas). Mais il est également dirigé vers la commande suivante. Je pense que ce qui se passe dans ce cas dépend en fait de la mise en œuvre. Par exemple sur ma bash 4.2.28, rien n'est jamais écrit pour sous-shell stdin. Sur zsh, cela fonctionne de manière fiable comme vous le souhaitez (en imprimant à la fois la première ligne de ps et les lignes recherchées), chaque fois que j'essaye,
la source
Un peu hackish, mais voici ma solution, sous la forme d'une
psgrep()
fonction shell que j'utilise:Redirigez la
ps
ligne d'en-tête surSTDERR
, puisgrep
surSTDOUT
, mais supprimez d'abord lagrep
commande elle-même, pour éviter la ligne "bruit" qui se crée d'grep
elle-même:la source