Warum funktioniert die BASH-Prozessersetzung bei einigen Befehlen nicht?

29

Gelegentlich funktioniert die Prozessersetzung nicht wie erwartet. Hier ist ein Beispiel:

Eingang:

gcc <(echo 'int main(){return 0;}')

Ausgabe:

/dev/fd/63: file not recognized: Illegal seek
collect2: error: ld returned 1 exit status

Eingang:

Aber es funktioniert wie erwartet, wenn es mit einem anderen Befehl verwendet wird:

grep main <(echo 'int main(){return 0;}')

Ausgabe:

int main(){return 0;}

Ich habe ähnliche Fehler bei anderen Befehlen festgestellt (dh der Befehl, der die Datei von der Prozessersetzung erwartet, kann nicht verwendet werden /dev/fd/63oder ähnliches). Dieser Fehler gccist nur der jüngste. Gibt es eine allgemeine Regel, die ich beachten sollte, um festzustellen, wann die Prozessersetzung auf diese Weise fehlschlägt und nicht verwendet werden sollte?

Ich benutze diese BASH-Version unter Ubuntu 12.04 (ich habe dies auch in Arch und Debian gesehen):
GNU bash, Version 4.3.11 (1) -release (i686-pc-linux-gnu)

Lotney
quelle
1
illegal seeksieht aus wie die Antwort - das |pipe, bashauf das das ausgeführte Programm zeigt, ist keine suchbare Datei. Wahrscheinlich, wenn Sie echo data | command /dev/fd/0bei einem Programm nicht erfolgreich sein können, haben Sie ähnliches Glück w / <(cmd). Es wird keine Datei auf der Festplatte bereitgestellt. Es wird lediglich ein Argument ersetzt, das auf einen Pipe-Dateideskriptor verweist.
mikeserv
2
In diesem speziellen Fall kann gcc zwar Standardeingaben akzeptieren, verwendet jedoch (standardmäßig) die Dateinamenerweiterung, um die Sprache zu bestimmen. Also versuche es gcc -xc <(echo 'int main(){return 0;}')(was die Sprache Cexplizit einstellt ).
Steeldriver
Ich wurde hier als Antwort auf meine eigene Frage verwiesen, was wahrscheinlich ein weiteres Beispiel dafür ist. superuser.com/questions/1243405 . Vielen Dank, dass Sie die Frage besser formuliert haben, als ich konnte.
Jonathan Hartley

Antworten:

33

Die Prozessersetzung führt zu einer speziellen Datei (wie /dev/fd/63in Ihrem Beispiel), die sich wie das Leseende einer Named Pipe verhält. Diese Datei kann geöffnet und gelesen, aber nicht geschrieben, nicht gesucht werden.

Befehle, die ihre Argumente als reine Streams behandeln, funktionieren, während Befehle, die erwarten, in Dateien zu suchen, die sie erhalten (oder in die sie schreiben), nicht funktionieren. Die Art des Befehls , dass Wille Arbeit ist das, was in der Regel einen Filter betrachtet wird: cat, grep, sed, gzip, awk, etc ... Ein Beispiel für einen Befehl, der nicht funktionieren ist ein Editor wie vioder eine Dateioperation wie mv.

gccmöchte in der Lage sein, auf seine Eingabedateien wahlfrei zuzugreifen, um festzustellen, in welcher Sprache sie geschrieben sind. Wenn Sie stattdessen gcceinen Hinweis auf die Sprache der Eingabedatei geben , ist es hilfreich, die Datei zu streamen:

gcc -x c <(echo 'int main(){return 0;}')

Die einfachere und einfachere Form ohne Prozessersetzung funktioniert auch:

echo 'int main(){return 0;}' | gcc -x c -

Beachten Sie, dass dies nicht spezifisch für ist bash. Alle Shells, die die Prozessersetzung unterstützen, verhalten sich gleich.

Celada
quelle
+1 für die gcc-Problemumgehung, aber ich bin mir nicht sicher, was Ihren Punkt in Bezug auf Dateien betrifft. Das <()Format sollte in jeder Hinsicht wie eine Datei wirken. Tatsächlich kenne ich keine Befehle, die eine Datei erwarten, mit der ich nicht zufrieden bin <(). Diejenigen , die keine Arbeit tun , sind diejenigen , die Datei erwarten Namen , keine Dateien. Erwartet beispielsweise grep -feine Datei und funktioniert einwandfrei mit <().
Terdon
4
@terndon Natürlich <()erzeugt eine Datei Namen (das Konstrukt erweitert , um /proc/self/fd/somethingauf meinem System). Dieser Name verhält sich beim Öffnen wie das Leseende einer Named Pipe ( S_IFIFO) und nicht wie eine reguläre Datei ( S_IFREG), die unterstützt wird, read()andere jedoch nicht seek().
Celada
7
Beachten Sie, dass zsheine dritte Form der Prozessersetzung unterstützt wird, die zu diesem Zweck temporäre Dateien verwendet:gcc =(echo 'int main(){return 0;}')
Stéphane Chazelas,
vielleicht verwandt , aber funktioniert mit <(echo '...')aber nicht mit <(git show ...). eine Idee, warum das sein könnte?
Jörn Hees
2
GCC führt keinen "Direktzugriff auf seine Eingabedateien durch, um festzustellen, in welcher Sprache sie geschrieben sind." Es wird nur die Dateinamenerweiterung angezeigt. Wenn der Dateiname keine Erweiterung hat (oder eine, die nicht erkannt wird), geht GCC davon aus, dass es sich bei der Datei um eine Objektdatei oder ein Linkerskript handelt, und übergibt sie an ld(wodurch Objektformate erkannt werden ). -xist kein Hinweis; Es ist eine Erklärung. Wenn Sie dies angeben -x f95, leitet GCC die Datei unabhängig von ihrem Namen oder Inhalt an den Fortran-95-Compiler weiter. Siehe gcc.gnu.org/onlinedocs/gcc-8.1.0/gcc/Overall-Options.html
rici