J'ai un binaire (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 ça ne marche pas avec un tuyau. Dans les 3 cas, le contenu du fichier est attribué à l'entrée standard de binaire (de différentes manières):
- bash lit le fichier et le donne à stdin de binaire
- bash lit les lignes de stdin (jusqu'à EOF) et le donne à stdin de binaire
- cat lit et met les lignes du fichier sur stdout, bash les redirige vers stdin de binaire
Le binaire ne devrait pas remarquer la différence entre ces 3 dans la mesure où je l’ai compris. Quelqu'un peut-il expliquer pourquoi le 3ème cas ne fonctionne pas?
BTW: L'erreur donnée par le binaire est:
20170116 / 125624.689 - U3000011 Impossible de lire le fichier de script '', code d'erreur '14'.
Mais ma question principale est la suivante: quelle différence y a-t-il entre un programme comportant 3 options?
Voici quelques détails supplémentaires: J'ai réessayé avec strace et il y avait en fait des erreurs ESPIPE (recherche illégale) de lseek suivies de EFAULT (adresse incorrecte) de lecture juste avant le message d'erreur.
Le binaire que j'ai essayé de contrôler avec un script ruby (sans utiliser de fichiers temporaires) fait partie du callapi d' Automic (UC4) .
la source
isatty()
retourne faux sera un fichier à rechercher ou à masquer ...cat
obturateur. Il semble que vous ne puissiez pas l'utiliser pour combiner deux fichiers, contrairement à l'utilisation prévue.Réponses:
Dans
binary
'stdin' est le fichier ouvert en mode lecture seule. Notez quebash
cela ne lit pas le fichier du tout, il l’ouvre simplement pour le lire sur le descripteur de fichier 0 (stdin) du processusbinary
dans lequel il s’exécute .Dans:
En fonction du shell,
binary
le stdin sera soit un fichier temporaire supprimé (AT & T ksh, zsh, bash ...) qui contient ce quitest\n
est mis ici par le shell ou l'extrémité de lecture d'un tuyau (dash
,yash
; et le shell écrittest\n
en parallèle à l'autre bout du tuyau). Dans votre cas, si vous utilisezbash
, ce sera un fichier temporaire.Dans:
Selon le shell,
binary
le stdin sera soit l'extrémité de lecture d'un tuyau, soit l'une des extrémités d'une paire de sockets dont le sens d'écriture a été arrêté (ksh93) etcat
écrit le contenu defile
l'autre extrémité.Lorsque stdin est un fichier normal (temporaire ou non), il est utilisable.
binary
peut aller au début ou à la fin, rembobiner, etc. Il peut égalementioctl()s
créer une carte , en faire comme FIEMAP / FIBMAP (si vous utilisez<>
plutôt que de<
, il pourrait tronquer / perforer des trous, etc.).Les pipes et les paires de sockets, quant à eux, sont un moyen de communication inter-processus, il n’ya pas grand
binary
chose à faire à côtéread
des données (bien qu’il existe aussi des opérations telles que desioctl()
s spécifiques à un pipe que cela pourrait faire sur eux et non sur des fichiers normaux) .La plupart du temps, il est la capacité manquante à
seek
qui provoque des applications à l' échec / se plaignent lorsque l'on travaille avec des tuyaux, mais il pourrait être l' un des autres appels système qui sont valides sur des fichiers normaux mais pas sur les différents types de fichiers (commemmap()
,ftruncate()
,fallocate()
) . Sous Linux, il existe également une grande différence de comportement lorsque vous ouvrez/dev/stdin
lorsque le fd 0 est sur un canal ou sur un fichier normal.Il y a beaucoup de commandes là - bas qui ne peut traiter que adressable fichiers, mais quand c'est le cas, qui est généralement pas pour les fichiers ouverts sur leur stdin.
unzip
doit lire l'index stocké à la fin du fichier, puis chercher dans le fichier pour lire les membres de l'archive. Mais ici, le fichier (normal dans le premier cas, pipe dans le second) est donné comme argument de cheminunzip
et l'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 fichiers zip à partir de son stdin. stdin est principalement utilisé pour l'interaction utilisateur.Si vous l'exécutez
binary
sans redirection à l'invite d'un shell interactif s'exécutant dans un émulateur de terminal, alorsbinary
stdin sera hérité de son parent le shell, qui l'aura lui-même hérité de son parent l'émulateur de terminal et sera un pty périphérique ouvert en mode lecture + écriture (quelque chose comme/dev/pts/n
).Ces appareils ne sont pas recherchés non plus. Donc, si cela
binary
fonctionne correctement lors de la saisie du terminal, le problème n’est peut-être pas lié à la recherche.Si ce numéro 14 est censé être un errno (un code d'erreur défini par des appels système défaillants), sur la plupart des systèmes, il s'agirait de
EFAULT
( adresse incorrecte ). L'read()
appel système échouerait avec cette erreur s'il était invité à lire dans une adresse mémoire inscriptible. Cela serait indépendant du fait que le fd lise les données des points dans un pipe ou un fichier normal et indiquerait généralement un bogue 1 .binary
détermine éventuellement le type de fichier ouvert sur son stdin (avecfstat()
) et se heurte à un bogue alors qu'il ne s'agit ni d'un fichier normal ni d'un périphérique tty.Difficile à dire sans en savoir plus sur l'application. L'exécuter sous
strace
(outruss
/tusc
équivalent 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 le commentaire de votre question semble fort plausible ici. En le citant:
la source
./binary < file
est recherchée!open
édité et qui se comporte comme n'importe quel fichier qui a étéopen
édité. Il se trouve que cela a simplement été hérité d'un processus parent, mais ce n'est pas si rare.open("/proc/self/fd/0", O_RDWR)
marche même sur des fichiers supprimés. Silly moi: P.echo foo>foo; (sleep 0.5; ll -L /proc/self/fd/0; strace ./a.out; ll -L /proc/self/fd/0) < foo & sleep 0.1 && rm foo
annule les liensfoo
avant que a.out soit exécuté avec son stdin redirigéfoo
.Voici un exemple de programme simple qui illustre la réponse de Stéphane Chazelas en utilisant
lseek(2)
:Essai:
Les pipes ne sont pas recherchées, et c'est un endroit où un programme peut se plaindre de pipes.
la source
Le tuyau et la redirection sont des animaux différents, pour ainsi dire. Lorsque vous utilisez
here-doc
redirection (<<
) ou stdin,<
le texte ne sort pas de nulle part - il entre dans un descripteur de fichier (ou dans un fichier temporaire, si vous préférez), et c’est là que le stdin du binaire sera pointé.Plus précisément, voici un extrait du
bash's
code source, fichier redir.c (version 4.3):Ainsi, étant donné que la redirection peut en principe être traitée comme un fichier, les fichiers binaires peuvent y naviguer ou
seek()
passer facilement d’un fichier à l’autre en passant à n’importe quel octet du fichier.Les pipes, étant donné qu’elles sont des tampons de 64 Ko (au moins sous Linux) avec des écritures de 4096 octets ou moins garanties comme étant atomiques, ne peuvent pas être recherchées, c’est-à-dire que vous ne pouvez pas y naviguer librement - vous ne pouvez les lire que séquentiellement. Une fois, j'ai implémenté la
tail
commande en python. 29 millions de lignes de texte peuvent être recherchées en microsecondes si elles sont redirigées, mais sicat
vous le faites via un tuyau, eh bien, rien ne peut être fait - tout doit donc être lu de manière séquentielle.Une autre possibilité est que le binaire puisse vouloir ouvrir un fichier spécifiquement et ne pas recevoir d'entrée d'un tuyau. Cela se fait généralement via
fstat()
un appel système et en vérifiant si l'entrée provient d'unS_ISFIFO
type de fichier (ce qui signifie un canal / un canal nommé).Votre binaire spécifique, puisque nous ne savons pas ce que c'est, tente probablement de chercher, mais ne peut pas chercher de canal. Il est recommandé de consulter sa documentation pour savoir ce que signifie exactement le code d'erreur 14.
REMARQUE : Certains shells, tels que dash (Debian Almquist Shell, par défaut
/bin/sh
sous Ubuntu), implémentent lahere-doc
redirection avec les canaux en interne . Il est donc possible qu'ils ne soient pas accessibles. Le point reste le même - les canaux sont séquentiels et ne peuvent pas être parcourus facilement, ce qui entraînerait des erreurs.la source
dash
font. Cette réponse explique le comportement observé avec bash, mais ce comportement n'est apparemment pas garanti pour les autres coques.dash
sur mon système. Je n'étais pas au courant de cela auparavant. Merci de signalerfstat()
stdin pour vérifier s’il s’agissait d’un tuyau.stat
prend un chemin. Mais en réalité,lseek
le moyen le plus raisonnable de déterminer si un fd est recherché après s’être déjà ouvert est d’essayer de le faire.La principale différence réside dans la gestion des erreurs.
Dans le cas suivant, l'erreur est signalée
Dans le cas suivant, l'erreur n'est pas signalée.
Avec bash, vous pouvez toujours utiliser PIPESTATUS:
Mais il n'est disponible qu'immédiatement après l'exécution de la commande:
Il existe une autre différence lorsque nous utilisons des fonctions de shell à la place des fichiers binaires. 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'lastpipe
option est activée et qu'ellebash
n'est pas interactive), le changement de variables n'a donc aucun effet dans le shell parent:la source
>
est effectuée par le shell, mais avec pipe, elle est effectuée par une commande qui produit du texte. D'ACCORD. Mais dans cette question spécifique, OP utilise un fichier existant, ce n'est donc pas le problème, et l'erreur est clairement produite par le binaire.