
Der folgende Bash-Fu-Code funktioniert unter Linux einwandfrei, bricht jedoch unter MacOS ab:
files="foo bar"
echo PROG 1
for file in $files
do
echo $file | tee -a tempfile.txt
done
sort -u tempfile.txt
echo PROG 2
function trick {
for file in $files
do
echo $file | tee -a $1
done
}
trick >(sort -u)
Der Fehler ist:
PROG 1
foo
bar
bar
foo
PROG 2
tee: /dev/fd/63: Bad file descriptor
foo
tee: /dev/fd/63: Bad file descriptor
bar
Unter Linux PROG 2
werden die gleichen Zeilen wie PROG 1
ohne Fehler geschrieben. Unter MacOS scheint der Pipe-Handle entweder geschlossen oder nicht übernommen zu sein.
Das obige ist das minimierte Beispiel, um das Problem zu reproduzieren. In Wirklichkeit verarbeite ich sowohl die Tie-Ausgabe als auch den umgeleiteten Handle stark. Etwas im Sinne von
function trick {
for file in $files
do
echo $file | tee -a $1 | grep -Eo "^.."
done
}
trick >(sort -u | sed 's|o|x|g')
Der Code funktioniert nicht in Bash 4.1, aber in Bash 4.4 in mehreren Distributionen (Arch, Ubuntu und Debian).
Antwort1
macOS wird mit einer sehr alten Version von ausgeliefert bash
. Dieser Fehler (dass der Dateideskriptor für diese Prozesssubstitution vor dem Aufruf tee
in diesem Kontext geschlossen wird)¹ wurde in neueren Versionen behoben. Unter Linux tritt das gleiche Problem auf (mit einer anderen Fehlermeldung, da es /dev/fd/x
dort anders implementiert ist), wenn dort Bash 3.2 verwendet wird.
Hier könnten Sie stattdessen zsh
oder verwenden ksh93
. Es ist bash
ohnehin eine gute Idee, dies zu vermeiden, daes wartet nicht auf Prozesse bei Prozessersetzungen(zsh wartet auf sie, ksh93 kann angewiesen werden, dies wait
für sie zu tun).
Beachten Sie, dass selbst die neueste Version (zum Zeitpunkt des Schreibens 4.4.12) bash
noch einige Fehler enthält, wie:
$ bash -c 'eval cat <(echo test)'
test # OK but:
$ bash -c 'eval "echo foo;cat" <(echo test)'
foo
cat: /dev/fd/63: No such file or directory
$ bash -c 'eval f=<(echo test) "; cat \$f"'
cat: /dev/fd/63: No such file or directory
und manche werden immer noch durch Pipes ausgelöst, wie:
$ cat file
echo "$1"
cat "$1"
$ bash -c 'source ./file <(echo test)'
/dev/fd/63
test # OK but:
$ cat file2
echo "$1" | wc -c
cat "$1"
$ bash -c 'source ./file2 <(echo test)'
11
cat: /dev/fd/63: No such file or directory
¹ bash schließt diesen Dateideskriptor, sobald eine Pipeline vorhanden ist. Ein kürzerer Reproduzierer:
$ bash -c 'f() { :; cat "$1"; }; f <(echo OK)'
OK
$ bash -c 'f() { :|:; cat "$1"; }; f <(echo test)'
cat: /dev/fd/63: No such file or directory