Pourquoi certaines commandes ne lisent-elles pas à partir de leur entrée standard?

19

Je me demande quand nous devrions utiliser le pipeline et quand nous ne devrions pas.

Dites par exemple, pour tuer certains processus qui gèrent des fichiers pdf, les éléments suivants ne fonctionneront pas en utilisant le pipeline:

ps aux | grep pdf | awk '{print $2}'|kill

Au lieu de cela, nous ne pouvons le faire que de la manière suivante:

kill $(ps aux| grep pdf| awk '{print $2}')

ou

ps aux | grep pdf | awk '{print $2}'| xargs kill

Selon man bash(version 4.1.2):

The standard output of command is connected via a pipe to the standard input of command2.

Pour le scénario ci-dessus:

  • le stdin de grepest le stdout de ps. Ça marche.
  • le stdin de awkest le stdout de grep. Ça marche.
  • le stdin de killest le stdout de awk. Ça ne marche pas.

Le stdin de la commande suivante obtient toujours une entrée du stdout de la commande précédente.

  • Pourquoi ça ne marche pas avec killou rm?
  • Quelle est la différence entre kill, rmentrée avec grep, awkentrée?
  • Y a-t-il des règles?
sylye
la source
1
Ce n'est pas une réponse , mais vous voudrez peut - être jeter un oeil sur les pgrep, pkillet les killallcommandes.
terdon
2
@terdon: J'utilise simplement le scénario ci-dessus pour montrer le problème du pipeline, je le comprends pgrepet le reste peut parfaitement y parvenir :)
sylye

Réponses:

17

Il existe deux façons courantes de fournir des contributions aux programmes:

  • fournir des données à STDIN sur les processus
  • spécifier des arguments de ligne de commande

killutilise uniquement des arguments de ligne de commande. Il ne lit pas depuis STDIN. Les programmes aiment grepet awklisent depuis STDIN (si aucun nom de fichier n'est donné comme arguments de ligne de commande) et traitent les données selon leurs arguments de ligne de commande (modèle, instructions, drapeaux, ...).

Vous pouvez uniquement diriger vers STDIN d'autres processus, pas vers des arguments de ligne de commande.

La règle courante est que les programmes utilisent STDIN pour traiter une quantité arbitraire de données. Tous les paramètres d'entrée supplémentaires ou, s'il n'y en a généralement que peu, sont passés par des arguments de ligne de commande. Si la ligne de commande peut devenir très longue, par exemple pour de longs awktextes de programme, il est souvent possible de les lire à partir de fichiers de programme supplémentaires ( -foption de awk).

Pour utiliser le STDOUT des programmes comme arguments de ligne de commande, utilisez $(...)ou en cas de beaucoup de données xargs. findpeut également cela directement avec -exec ... {} +.

Pour être complet: Pour écrire des arguments de ligne de commande dans STDOUT, utilisez echo.

jofel
la source
1
Comment savons-nous qu'une commande ne prendra que des arguments mais pas STDIN? Existe-t-il une méthode systématique ou programmatique plutôt que de deviner ou de lire à partir de la page de manuel? En ne lisant que la page de manuel, je n'ai pas pu obtenir d'indices précis pour savoir si la commande peut ou non prendre STDIN, car STDIN fait également partie des arguments de la façon dont une page de manuel est présente. Par exemple, gzipdans le SYNOPSIS, il n'a pas dit qu'il devait prendre un FILENAME en entrée. Je cherche s'il existe un moyen plus systématique de déterminer cela.
sylye
Il y a aussi l'argument "-" qui signifie "stdin" (ou "stdout") pour certaines commandes.
Emmanuel
Ne xargsvous permettra- t-il pas précisément de "diriger vers des arguments de ligne de commande"?
T. Verron
@ T.Verron oui, c'est la tâche de xargs. Il appelle la commande si nécessaire plusieurs fois (la taille de la ligne de commande est limitée) et propose de nombreuses autres options.
jofel le
2
Le texte de la description décrira comment vous pouvez utiliser le programme. Par exemple, gzip dit: "Le programme gzip compresse et décompresse les fichiers en utilisant le codage Lempel-Ziv (LZ77). Si aucun fichier n'est spécifié, gzip se compressera à partir de l'entrée standard, ou décompressera à la sortie standard." Si une page de manuel ne mentionne pas d'entrée standard, elle ne l'utilisera pas.
Alan Shutko
16

C'est une question intéressante, et elle traite d'une partie de la philosophie Unix / Linux.

Alors, quelle est la différence entre les programmes tels que grep, sed, sortd'une part et kill, rm, lsd'autre part? Je vois deux aspects.

L' aspect filtre

  • Le premier type de programmes est également appelé filtres . Ils prennent une entrée, à partir d'un fichier ou de STDIN, la modifient et génèrent une sortie, principalement vers STDOUT. Ils sont destinés à être utilisés dans un tube avec d'autres programmes comme sources et destinations.

  • Le deuxième type de programmes agit sur une entrée, mais la sortie qu'ils donnent n'est souvent pas liée à l'entrée. killn'a pas de sortie quand il fonctionne régulièrement, non plus ls. Ils ont juste une valeur de retour pour montrer le succès. Ils ne prennent normalement pas d'entrée de STDIN, mais donnent principalement la sortie de STDOUT.

Pour des programmes comme ls, l'aspect du filtre ne fonctionne pas si bien. Il peut certainement avoir une entrée (mais n'en a pas besoin), et la sortie est étroitement liée à cette entrée, mais elle ne fonctionne pas comme un filtre. Cependant, pour ce type de programmes, l'autre aspect fonctionne toujours:

L' aspect sémantique

  • Pour les filtres, leur entrée n'a pas de signification sémantique . Ils ne font que lire des données, modifier des données, produire des données. Peu importe qu'il s'agisse d'une liste de valeurs numériques, de noms de fichiers ou de code source HTML. La signification de ces données n'est donnée que par le code que vous fournissez au filtre: l'expression régulière de grep, les règles de awkou le programme Perl.

  • Pour d'autres programmes, comme killou ls, leur entrée a un sens , une dénotation . killattend les numéros de processus, lsattend les noms de fichier ou de chemin. Ils ne peuvent pas gérer des données arbitraires et ils ne sont pas censés le faire. Beaucoup d'entre eux n'ont même pas besoin d'entrée ou de paramètres, comme ps. Ils ne lisent pas normalement depuis STDIN.

On pourrait probablement combiner ces deux aspects: Un filtre est un programme dont l'entrée n'a pas de signification sémantique pour le programme.

Je suis sûr d'avoir lu quelque part sur cette philosophie, mais je ne me souviens d'aucune source pour le moment, désolé. Si quelqu'un a des sources présentes, n'hésitez pas à les modifier.

Dubu
la source
5

Il n'y a pas de "règles" en tant que telles. Certains programmes prennent des entrées de STDIN, et d'autres non. Si un programme peut recevoir des entrées de STDIN, il peut être dirigé vers, sinon, il ne le peut pas.

Vous pouvez normalement dire si un programme prendra ou non une contribution en réfléchissant à ce qu'il fait. Si le travail du programme est de manipuler en quelque sorte le contenu d'un fichier (par exemple grep, sed, awketc.), il faut normalement l' entrée STDIN. Si son travail consiste à manipuler le fichier lui - même (par exemple mv, rm, cp) ou d' un processus (par exemple kill, lsof) ou à l' information de retour sur quelque chose (par exemple top, find, ps) il ne fonctionne pas.

Une autre façon de penser à ce sujet est la différence entre les arguments et les entrées. Par exemple:

mv foo bar

Dans la commande ci-dessus, mvn'a pas d'entrée en tant que telle. Ce qui lui a été donné, ce sont deux arguments. Il ne sait ni ne se soucie de ce qui se trouve dans aucun des fichiers, il sait juste que ce sont ses arguments et il doit les manipuler.

D'autre part

sed -e 's/foo/bar/' < file
--- -- ------------   ----
 |   |       |          |-> input
 |   |       |------------> argument        
 |   |--------------------> option/flag/switch
 |------------------------> command

Ici, seda été donné une entrée ainsi qu'un argument. Puisqu'il prend une entrée, il peut la lire depuis STDIN et il peut être redirigé vers.

Cela devient plus compliqué lorsqu'un argument peut être l'entrée. Par exemple

cat file

Voici filel'argument qui a été donné cat. Pour être précis, le nom du fichier fileest l'argument. Cependant, comme il cats'agit d'un programme qui manipule le contenu des fichiers, son entrée est tout ce qui se trouve à l'intérieur file.

Cela peut être illustré à l'aide d' straceun programme qui suit les appels système effectués par les processus. Si nous exécutons cat foovia strace, nous pouvons voir que le fichier fooest ouvert:

$ strace cat foo 2| grep foo
execve("/bin/cat", ["cat", "foo"], [/* 44 vars */]) = 0
open("foo", O_RDONLY)     

La première ligne ci-dessus montre que le programme a /bin/catété appelé et ses arguments étaient catet foo(le premier argument est toujours le programme lui-même). Plus tard, l'argument a fooété ouvert en mode lecture seule. Maintenant, comparez cela avec

$ strace ls foo 2| grep foo 
execve("/bin/ls", ["ls", "foo"], [/* 44 vars */]) = 0
stat("foo", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
lstat("foo", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
write(1, "foo\n", 4foo

Ici aussi, lss'est pris et foocomme argument. Cependant, il n'y a pas d' openappel, l'argument n'est pas traité comme une entrée. Au lieu de cela, lsappelle la statbibliothèque du système (qui n'est pas la même chose que la statcommande) pour obtenir des informations sur le fichier foo.

En résumé, si la commande que vous exécutez lira son entrée, vous pouvez y accéder, sinon, vous ne pouvez pas.

terdon
la source
0
  • Pourquoi ça ne marche pas avec kill ou rm?

killet rmn'ont pas besoin de STDIN.

  • Quelle est la différence entre kill, rm input avec grep, awk input?

Pour killet rm, les utilisateurs fournissent leurs informations personnalisées comme argument et $(cmd)aident à prendre le STDOUT cmdet à le convertir en argument info.

Pour grepet awk, les utilisateurs fournissent des arguments et en plus, également STDINou un fichier normal qui sera traité par la commande. STDINpeut être passé avec le pipeline |ou en entrant manuellement.

  • Y a-t-il des règles?

Lisez le manuel ou les codes source. Et si vous ne trouvez rien dont vous avez besoin, vous pouvez faire un test simple mais peut-être dangereux:

Entrez simplement la commande qui vous intéresse, avec des arguments que vous avez déjà compris, et voyez si la commande se met en pause (rien ne se passe). Si elle une pause, il attend réellement pour STDIN (vous pouvez essayer catet echode voir les différents). Vous tapez manuellement Ctrl-Det la commande va de l'avant (afficher les résultats ou les erreurs) et retourne. Une telle commande nécessite STDIN dans cette situation (avec les arguments que vous fournissez).

La même commande peut ne pas avoir besoin de STDIN dans différentes situations (par exemple, catattend STDIN mais cat file.txtpas).

Alex Huang
la source