Iteratives Schreiben in dieselbe Prozessersetzungsdatei auf MacOs

Iteratives Schreiben in dieselbe Prozessersetzungsdatei auf MacOs

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 2werden die gleichen Zeilen wie PROG 1ohne 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 teein diesem Kontext geschlossen wird)¹ wurde in neueren Versionen behoben. Unter Linux tritt das gleiche Problem auf (mit einer anderen Fehlermeldung, da es /dev/fd/xdort anders implementiert ist), wenn dort Bash 3.2 verwendet wird.

Hier könnten Sie stattdessen zshoder verwenden ksh93. Es ist bashohnehin eine gute Idee, dies zu vermeiden, daes wartet nicht auf Prozesse bei Prozessersetzungen(zsh wartet auf sie, ksh93 kann angewiesen werden, dies waitfür sie zu tun).

Beachten Sie, dass selbst die neueste Version (zum Zeitpunkt des Schreibens 4.4.12) bashnoch 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

verwandte Informationen