Quelle est la différence entre "file chat | ./binary "et" ./binary <file "?

J'ai un binary (que je ne peux pas modifier) ​​et je peux faire:

./binary < file 

Je peux aussi faire:

 ./binary << EOF > "line 1 of file" > "line 2 of file" ... > "last line of file" > EOF 

Mais

 cat file | ./binary 

me donne une erreur. Je ne sais pas pourquoi cela ne fonctionne pas avec une pipe. Dans les 3 cas, le contenu du file est donné à l'input standard du binary (de différentes manières):

  1. bash lit le file et le donne à stdin de binary
  2. bash lit les lignes de stdin (jusqu'à EOF) et les donne à stdin de binary
  3. cat lit et met les lignes de file à stdout, bash les redirige vers stdin de binary

Le binary ne devrait pas remarquer la différence entre ces 3 pour autant que je l'ai compris. Quelqu'un peut-il m'expliquer pourquoi le troisième cas ne fonctionne pas? Je vous remercie!

BTW: L'erreur donnée par le binary est

20170116 / 125624.689 – U3000011 Impossible de lire le file de script '', code d'erreur '14'.

Mais ma question principale est la suivante: comment y a-t-il une différence pour n'importe quel programme avec ces 3 options?

PS: Merci à tous pour votre aide et vos explications détaillées !!!

Voici quelques détails supplémentaires: Je l'ai essayé à nouveau avec strace et là où en fait quelques erreurs ESPIPE (search illégale) de lseek suivies par EFAULT (mauvaise adresse) de lire juste avant le message d'erreur.

Le binary que j'ai essayé de contrôler avec un script ruby ​​(sans utiliser de files temporaires) fait partie du callapi d' Automic (UC4) .

    Dans

     ./binary < file 

    Le stdin du binary est le file ouvert en mode lecture seule. Notez que bash ne lit pas du tout le file, il l'ouvre juste pour lire sur le descripteur de file 0 (stdin) du process dans lequel il exécute le binary .

    Dans:

     ./binary << EOF test EOF 

    Selon le shell, le stdin du binary sera soit un file temporaire supprimé (AT & T ksh, zsh, bash …) qui contient le test\n mis par le shell ou la fin de la lecture d'un tube ( dash , yash ; et la coquille écrit test\n en parallèle à l'autre extrémité du tuyau). Dans votre cas, si vous utilisez bash , ce serait un file temporaire.

    Dans:

     cat file | ./binary 

    Selon le shell, le stdin du binary sera soit la fin de lecture d'un tube, soit une extrémité d'une paire de socket où le sens d'écriture a été arrêté (ksh93) et que cat écrit le contenu du file à l'autre extrémité.

    Lorsque stdin est un file régulier (temporaire ou non), il est recherché. binary peut aller au début ou à la fin, rembobiner, etc. Il peut aussi le faire par mmap, faire quelques ioctl()s comme FIEMAP / FIBMAP (si <> au lieu de < , il pourrait tronquer / perforer dedans, etc.

    les pipes et les paires de socket sont des moyens de communication inter-process, il n'y a pas beaucoup de binary côté de la read des données (bien qu'il y ait aussi des opérations comme des ioctl() pas sur les files réguliers).

    La plupart du time, c'est la capacité manquante de seek qui fait échouer / se plaindre les applications quand on travaille avec des pipes, mais ce pourrait être n'importe lequel des autres appels système valides sur des files normaux mais pas sur différents types de files mmap() , ftruncate() , fallocate() ). Sous Linux, il y a aussi une grande différence de comportement lorsque vous ouvrez /dev/stdin alors que le fd 0 est sur un canal ou sur un file normal.

    Il existe de nombreuses commands qui ne peuvent traiter que des files recherchables, mais quand c'est le cas, ce n'est généralement pas pour les files ouverts sur leur stdin.

     $ unzip -l file.zip Archive: file.zip Length Date Time Name --------- ---------- ----- ---- 11 2016-12-21 14:43 file --------- ------- 11 1 file $ unzip -l <(cat file.zip) # more or less the same as cat file.zip | unzip -l /dev/stdin Archive: /proc/self/fd/11 End-of-central-directory signature not found. Either this file is not a zipfile, or it constitutes one disk of a multi-part archive. In the latter case the central directory and zipfile comment will be found on the last disk(s) of this archive. unzip: cannot find zipfile directory in one of /proc/self/fd/11 or /proc/self/fd/11.zip, and cannot find /proc/self/fd/11.ZIP, period. 

    unzip doit lire l'index stocké à la fin du file, puis chercher dans le file pour lire les membres de l'archive. Mais ici, le file (normal dans le premier cas, pipe dans le second) est donné comme argument de path pour unzip , et le unzip ouvre lui-même (généralement sur fd autre que 0) au lieu d'hériter d'un fd déjà ouvert par le parent. Il ne lit pas les files zip de son stdin. stdin est principalement utilisé pour l'interaction de l'user.

    Si vous exécutez votre binary sans redirection à l'invite d'un shell interactif s'exécutant dans un émulateur de terminal, alors le stdin du binary sera hérité de son parent, le shell lui-même l'a hérité de son parent l'émulateur de terminal et va être un périphérique pty ouvert en mode lecture + écriture (quelque chose comme /dev/pts/n ).

    Ces appareils ne sont pas recherchés non plus. Donc, si le binary fonctionne correctement lors de la saisie des données du terminal, il est possible que le problème ne concerne pas la search.

    Si ce 14 est censé être un errno (un code d'erreur défini par défaut d'appels système), alors sur la plupart des systèmes, ce serait EFAULT ( mauvaise adresse ). L'appel système read() échouerait avec cette erreur si demandé à lire dans une adresse memory qui n'est pas inscriptible. Cela serait indépendant de savoir si le fd pour lire datatables de points à un tuyau ou un file régulier et indiquerait généralement un bug 1 .

    binary détermine éventuellement le type de file ouvert sur son stdin (avec fstat() ) et s'exécute dans un bug quand ce n'est ni un file normal, ni un périphérique tty.

    Difficile à dire sans en savoir plus sur l'application. L' tusc sous strace (ou l'équivalent de truss / tusc sur votre système) pourrait nous aider à voir quel est l'appel système s'il y en a un qui échoue ici.


    1 Le scénario envisagé par Matthew Ife dans un commentaire à votre question semble beaucoup plausible ici. Le citant:

    Je soupçonne qu'il cherche à la fin du file pour get une taille de tampon pour lire datatables, manipulant mal le fait que la search ne fonctionne pas et tente d'allouer une taille négative (ne pas manipuler un mauvais malloc). Passer le tampon pour lire les fautes données au tampon n'est pas valide.

    Voici un exemple simple qui illustre la réponse de Stéphane Chazelas à l' aide de lseek(2) :

     #include <stdio.h> #include <sys/types.h> #include <unistd.h> int main(void) { int c; off_t off; off = lseek(0, 10, SEEK_SET); if (off == -1) { perror("Error"); return -1; } c = getchar(); printf("%c\n", c); } 

    Essai:

     $ make seek cc seek.c -o seek $ cat foo abcdefghijklmnopqrstuwxyz $ ./seek < foo k $ ./seek <<EOF > abcdefghijklmnopqrstuvwxyz > EOF k $ cat foo | ./seek Error: Illegal seek 

    Les tuyaux ne sont pas recherchés, et c'est un endroit où un programme pourrait se plaindre des tuyaux.

    Le tuyau et la redirection sont des animaux différents, pour ainsi dire. Lorsque vous utilisez la redirection de here-doc ( << ) ou la redirection de stdin < le text ne vient pas de nulle part – il va dans un descripteur de file (ou un file temporaire, si vous voulez), et c'est là que le binary stdin va pointer.

    Plus précisément, voici un extrait du code source de bash's , redir.c (version 4.3):

     /* Create a temporary file holding the text of the here document pointed to by REDIRECTEE, and return a file descriptor open for reading to the temp file. Return -1 on any error, and make sure errno is set appropriately. */ static int here_document_to_fd (redirectee, ri) 

    Ainsi, étant donné que la redirection peut essentiellement être traitée comme des files, les binarys peuvent les parcourir, ou seek() facilement dans le file, en sautant à n'importe quel octet du file.

    Les pipes, puisqu'elles sont des buffers de 64 KiB (au less sous Linux) avec des écritures de 4096 octets ou less garantis pour être atomiques, ne sont pas recherchables, c'est à dire que vous ne pouvez pas les naviguer librement – seulement lire séquentiellement. J'ai mis en place tail command de tail en python. 29 millions de lignes de text peuvent être recherchées en microsecondes si elles sont redirigées, mais si elles sont envoyées via le canal, il n'y a rien à faire – il faut donc les lire séquentiellement.

    Une autre possibilité est que le binary peut vouloir ouvrir un file spécifiquement, et ne veut pas recevoir d'input d'un tuyau. Il est généralement fait via l'appel système fstat() et vérifie si l'input provient d'un type de file S_ISFIFO (qui signifie pipe / pipe nommée).

    Votre binary spécifique, puisque nous ne soaps pas ce que c'est, tente probablement de chercher, mais ne peut pas chercher des tuyaux. Il est recommandé de consulter sa documentation pour savoir exactement ce que signifie le code d'erreur 14.

    NOTE : Certains shells, tels que dash (Debian Almquist Shell, par défaut /bin/sh sur Ubuntu) implémentent la redirection de ce here-doc avec des tuyaux en interne , donc ne peuvent pas être recherchés. Le point rest le même – les tuyaux sont séquentiels et ne peuvent pas être navigués facilement, et les tentatives de le faire entraînera des erreurs.

    La principale différence est dans la gestion des erreurs.

    Dans le cas suivant, l'erreur est signalée

     $ /bin/cat < z.txt -bash: z.txt: No such file or directory $ echo $? 1 

    Dans le cas suivant, l'erreur n'est pas signalée.

     $ cat z.txt | /bin/cat cat: z.txt: No such file or directory $ echo $? 0 

    Avec bash, vous pouvez toujours utiliser PIPESTATUS:

     $ cat z.txt | /bin/cat cat: z.txt: No such file or directory $ echo ${PIPESTATUS[0]} 1 

    Mais il est disponible seulement immédiatement après l'exécution de la command:

     $ cat z.txt | /bin/cat cat: z.txt: No such file or directory $ echo $? 0 $ echo ${PIPESTATUS[0]} 0 # oops ! 

    Il y a une autre différence, lorsque nous utilisons des fonctions shell au lieu de binarys. Dans bash , les fonctions faisant partie d'un pipeline sont exécutées dans des sous-shells (à l'exception du dernier composant de pipeline si l'option lastpipe est activée et bash est non interactive), de sorte que le changement de variables n'a aucun effet sur le shell parent:

     $ a=a $ b=b $ x(){ a=x;} $ y(){ b=y;} $ echo $a $b ab $ x | y $ echo $a $b ab $ cat t.txt | y $ echo $a $b ab $ x | cat $ echo $a $b ab $ x < t.txt $ y < t.txt $ echo $a $b xy