Ich habe eine Binärdatei (die ich nicht ändern kann) und kann Folgendes:
./binary < file
Ich kann auch:
./binary << EOF
> "line 1 of file"
> "line 2 of file"
...
> "last line of file"
> EOF
Aber
cat file | ./binary
gibt mir einen fehler. Ich weiß nicht, warum es mit einer Pfeife nicht funktioniert. In allen drei Fällen wird der Inhalt der Datei auf unterschiedliche Weise an die Standardeingabe von binary übergeben :
- bash liest die Datei und gibt sie an stdin of binary weiter
- bash liest Zeilen von stdin (bis EOF) und gibt sie an stdin of binary weiter
- cat liest und setzt die Dateizeilen auf stdout, bash leitet sie auf stdin von binary um
Die Binärdatei sollte den Unterschied zwischen diesen 3 nicht bemerken, soweit ich es verstanden habe. Kann jemand erklären, warum der 3. Fall nicht funktioniert?
Übrigens: Der durch die Binärdatei gegebene Fehler ist:
20170116 / 125624.689 - U3000011 Skriptdatei '', Fehlercode '14' konnte nicht gelesen werden.
Aber meine Hauptfrage ist, wie gibt es einen Unterschied für jedes Programm mit diesen 3 Optionen.
Hier sind einige weitere Details: Ich habe es erneut mit Strace versucht und es gab tatsächlich einige Fehler ESPIPE (Illegal seek) von lseek gefolgt von EFAULT (Bad address) von read direkt vor der Fehlermeldung.
Die Binärdatei, die ich mit einem Ruby-Skript zu steuern versuchte (ohne temporäre Dateien zu verwenden), ist Teil der Callapi von Automic (UC4) .
quelle
isatty()
falsch ist, eine durchsuchbare oder abbildbare Datei ist ...cat
Preventer. Es sieht so aus, als könnten Sie damit nicht zwei Dateien kombinieren, wie es die beabsichtigte Verwendung ist.Antworten:
Im
binary
's stdin ist die Datei, die im schreibgeschützten Modus geöffnet ist. Beachten Sie, dassbash
die Datei überhaupt nicht gelesen wird, sondern nur zum Lesen im Dateideskriptor 0 (stdin) des Prozesses geöffnet wird,binary
in dem sie ausgeführt wird .Im:
Abhängig von der Shell ist
binary
stdin entweder eine gelöschte temporäre Datei (AT & T ksh, zsh, bash ...), dietest\n
das von der Shell gesetzte Ende einer Pipe enthält , oder das Leseende einer Pipe (dash
,yash
; und die Shell schreibttest\n
parallel am anderen Ende der Leitung). In Ihrem Fallbash
wäre dies eine temporäre Datei.Im:
Abhängig von der Shell ist
binary
stdin entweder das Leseende einer Pipe oder ein Ende eines Socket-Paares, bei dem die Schreibrichtung heruntergefahren wurde (ksh93) undcat
der Inhalt vonfile
am anderen Ende geschrieben wird.Wenn stdin eine reguläre Datei ist (temporär oder nicht), kann danach gesucht werden.
binary
kann an den Anfang oder das Ende gehen, zurückspulen usw. Es kann es auch mappen, etwasioctl()s
wie FIEMAP / FIBMAP tun (wenn es<>
anstelle von verwendet wird<
, könnte es Löcher darin abschneiden / lochen usw.).Pipes und Socket-Paare hingegen sind ein Kommunikationsmittel zwischen Prozessen.
binary
Nebenread
den Daten gibt es nicht viel zu tun (obwohl es auch einige Operationen gibt, wie z. B. einige Pipe-spezifische Operationenioctl()
, die für sie und nicht für reguläre Dateien ausgeführt werden könnten). .Die meisten der Zeit, es ist die fehlende Möglichkeit,
seek
die Anwendungen scheitern / beschweren , wenn die Arbeit mit Rohren verursacht, aber es könnte eine der anderen Systemaufrufe, die auf reguläre Dateien gültig sind , aber nicht auf verschiedene Arten von Dateien (wiemmap()
,ftruncate()
,fallocate()
) . Unter Linux gibt es auch einen großen Unterschied im Verhalten, wenn Sie öffnen,/dev/stdin
während sich der fd 0 in einer Pipe oder in einer regulären Datei befindet.Es gibt viele Befehle, die sich nur mit durchsuchbaren Dateien befassen können, aber wenn dies der Fall ist, gilt dies im Allgemeinen nicht für die Dateien, die im Standardverzeichnis geöffnet sind.
unzip
muss den Index lesen, der am Ende der Datei gespeichert ist, und dann in der Datei nach den Archivmitgliedern suchen. Aber hier wird die Datei (regulär im ersten Fall, Pipe im zweiten Fall) als Pfadargument für angegebenunzip
undunzip
öffnet sie selbst (normalerweise auf fd ungleich 0), anstatt ein fd zu erben, das bereits vom übergeordneten Element geöffnet wurde. Es liest keine zip-Dateien von seinem Standard. stdin wird hauptsächlich für die Benutzerinteraktion verwendet.Wenn Sie
binary
Ihre eigene Shell ohne Umleitung ausführen,binary
während eine interaktive Shell in einem Terminalemulator ausgeführt wird, wird die Standard-ID von der übergeordneten Shell geerbt, die sie selbst von der übergeordneten Shell, dem Terminalemulator, geerbt hat pty-Gerät im Lese- und Schreibmodus öffnen (so etwas wie/dev/pts/n
).Diese Geräte sind auch nicht suchbar.
binary
Wenn die Eingabe über das Terminal einwandfrei funktioniert, liegt das Problem möglicherweise nicht in der Suche.Wenn die 14 gemeint ist eine errno sein (einen Fehlercode gesetzt durch Systemaufrufe nicht an ), dann auf den meisten Systemen, wäre die
EFAULT
( Bad - Adresse ). Derread()
Systemaufruf schlägt mit diesem Fehler fehl, wenn er zum Einlesen einer nicht beschreibbaren Speicheradresse aufgefordert wird. Das wäre unabhängig davon, ob der fd die Daten von Punkten in eine Pipe oder eine reguläre Datei liest und würde generell einen Bug 1 anzeigen .binary
Ermittelt möglicherweise den Typ der Datei, die im Standardverzeichnis (mitfstat()
) geöffnet ist, und stößt auf einen Fehler, wenn es sich weder um eine reguläre Datei noch um ein tty-Gerät handelt.Schwer zu sagen, ohne mehr über die Anwendung zu wissen. Laufen sie unter
strace
(odertruss
/tusc
äquivalent auf Ihrem System) könnte dazu beitragen , uns zu sehen , was der Systemaufruf, wenn überhaupt , die hier versagt.1 Das von Matthew Ife in einem Kommentar zu Ihrer Frage ins Auge gefasste Szenario klingt hier sehr plausibel. Zitiert ihn:
quelle
./binary < file
suchbar sind!open
und es verhält sich genauso wie jede Datei, die bearbeitet wurdeopen
. Es wurde zufällig von einem übergeordneten Prozess geerbt, aber das ist nicht so ungewöhnlich.open("/proc/self/fd/0", O_RDWR)
ja , funktioniert auch bei gelöschten dateien. Dumm mich: 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
hebt die Verknüpfung auf,foo
bevor a.out mit der von umgeleiteten stdin ausgeführt wirdfoo
.Hier ist ein einfaches Beispielprogramm, das die Antwort von Stéphane Chazelas anhand
lseek(2)
seiner Eingabe veranschaulicht :Testen:
Pipes sind nicht suchbar, und an dieser Stelle könnte sich ein Programm über Pipes beschweren.
quelle
Die Pipe und die Umleitung sind sozusagen verschiedene Tiere. Wenn Sie
here-doc
umleiten (<<
) oder stdin umleiten, kommt<
der Text nicht aus dem Nichts - er geht tatsächlich in einen Dateideskriptor (oder eine temporäre Datei, wenn Sie so wollen), und dort zeigt das stdin der Binärdatei.Im Einzelnen ist hier ein Auszug aus dem
bash's
Quellcode der Datei redir.c (Version 4.3):Da die Umleitung im Grunde genommen als Dateien behandelt werden kann, können die Binärdateien sie navigieren oder
seek()
einfach durch die Datei springen und zu einem beliebigen Byte der Datei springen.Pipes sind Pufferspeicher von 64 KB (zumindest unter Linux) mit Schreibzugriffen von 4096 Byte oder weniger, die garantiert atomar sind. Sie können also nicht frei navigieren, sondern nur sequentiell lesen. Ich habe einmal
tail
Befehl in Python implementiert . 29 Millionen Textzeilen können in Mikrosekunden gesucht werden, wenn sie umgeleitet werden, aber wenn siecat
über eine Pipe gesendet werden , ist nichts zu tun. Daher muss alles nacheinander gelesen werden.Eine andere Möglichkeit besteht darin, dass die Binärdatei eine bestimmte Datei öffnen und keine Eingabe von einer Pipe empfangen möchte. Dies geschieht normalerweise über einen
fstat()
Systemaufruf und überprüft, ob die Eingabe von einemS_ISFIFO
Dateityp stammt (der eine Pipe / Named Pipe bezeichnet).Ihre spezifische Binärdatei versucht wahrscheinlich zu suchen, kann aber keine Pipes suchen, da wir nicht wissen, was es ist. Es wird empfohlen, die Dokumentation zu Rate zu ziehen, um herauszufinden, was genau Fehlercode 14 bedeutet.
HINWEIS : Einige Shells, wie beispielsweise dash (Debian Almquist Shell, standardmäßig
/bin/sh
unter Ubuntu), implementierenhere-doc
die interne Umleitung von Pipes und sind daher möglicherweise nicht suchbar. Der Punkt bleibt derselbe - Pipes sind sequentiell und können nicht einfach navigiert werden, und Versuche, dies zu tun, führen zu Fehlern.quelle
dash
dies tun. Diese Antwort erklärt das beobachtete Verhalten bei Bash, aber dieses Verhalten ist anscheinend nicht für alle anderen Shells garantiert.dash
auf meinem System überprüft . Das war mir vorher nicht bewusst. Vielen Dank für den Hinweisfstat()
stdin verwenden, um zu prüfen, ob es sich um eine Pipe handelt.stat
nimmt einen Pfadnamen. Aber wirklich, nur zu versuchen,lseek
ist der wahrscheinlich vernünftigste Weg, um festzustellen, ob ein fd suchbar ist, nachdem er bereits geöffnet ist.Der Hauptunterschied liegt in der Fehlerbehandlung.
Im folgenden Fall wird der Fehler gemeldet
Im folgenden Fall wird der Fehler nicht gemeldet.
Mit bash können Sie weiterhin PIPESTATUS verwenden:
Es ist jedoch erst unmittelbar nach der Ausführung des Befehls verfügbar:
Es gibt einen weiteren Unterschied, wenn wir Shell-Funktionen anstelle von Binärdateien verwenden. In
bash
werden Funktionen, die Teil einer Pipeline sind, in Unterschalen ausgeführt (mit Ausnahme der letzten Pipelinekomponente, wenn dielastpipe
Option aktiviert undbash
nicht interaktiv ist), sodass die Änderung von Variablen in der übergeordneten Shell keine Auswirkungen hat:quelle
>
der Shell erfolgt, mit der Pipe jedoch mit einem Befehl, der Text erzeugt. OKAY. Bei dieser speziellen Frage verwendet OP jedoch eine vorhandene Datei, sodass dies nicht der Fall ist und der Fehler eindeutig durch die Binärdatei verursacht wird.