Warum funktioniert „cat "${1:-/dev/stdin} | ... &>/dev/null" in Bash, aber nicht in Dash?

Warum funktioniert „cat "${1:-/dev/stdin} | ... &>/dev/null" in Bash, aber nicht in Dash?

Skript:

#!/bin/sh
#
# reads stdin/file and copies it to clipboard
# clears it after 30s
#
cat "${1:-/dev/stdin}" | timeout 30 xclip -i -selection clipboard -r -verbose &>/dev/null &

Ich sehe, dass nur stdin nicht funktioniert (mit bash funktioniert es mit stdin/file).
PS: Verbose wird verwendet, damit xclip nicht als Daemon ausgeführt wird.

Antwort1

&>ist ein Bashismus, Sie müssen es >/dev/null 2>&1für POSIX-Shells ändern in

Antwort2

[Diese Antwort bezieht sich auf asynchrone Pipelines in Skripten. Informationen zum veralteten &>Bash-Operator und warum Sie ihn >output 2>&1stattdessen immer verwenden sollten, finden Sie unterveraltete und nicht mehr empfohlene Syntax]

#! /bin/sh
cat "${1:-/dev/stdin}" | ... &

Hier haben Sie eine Pipeline, die asynchron ausgeführt wird (weil sie durch beendet wurde &), und die von einem Skript gestartet wird, d. h. von einer Shell mit deaktivierter Jobsteuerung.

Entsprechend derStandard:

command1 & [command2 & ... ]

Die Standardeingabe für eine asynchrone Liste gilt, bevor explizite Umleitungen durchgeführt werden, als einer Datei zugewiesen, die dieselben Eigenschaften wie hat /dev/null.

Das Problem besteht darin, dass dash, ksh, mksh, yash, usw. „asynchrone Liste“ als jeden beliebigen Befehl, einschließlich einer Pipeline, interpretieren und die Standardeingabe des ersten Befehls von folgendem umleiten /dev/null:

$ echo foo | dash -c 'cat | tr fo FO & echo DONE'
DONE
$ echo | dash -c 'readlink /proc/self/fd/0 | cat & echo DONE'
DONE
/dev/null

Aber bashes wird nur als "einfacher Befehl" interpretiert und wird nur dann seine Standardeingabe umleiten, /dev/nullwenn esnichtTeil einer Pipeline:

$ echo foo | bash -c 'cat | tr fo FO & echo DONE'
DONE
FOO
$ echo | bash -c 'readlink /proc/self/fd/0 | cat & echo DONE'
DONE
pipe:[69872]
$ echo | bash -c 'readlink /proc/self/fd/0 & echo DONE'
DONE
/dev/null
$ bash -c 'cat | tr a A & echo DONE'
DONE
cat: -: Input/output error

zshleitet es nur um /dev/null, wenn die ursprüngliche Standardeingabe ein TTY ist, nicht, wenn es sich um einen anderen Dateityp handelt:

$ zsh -c 'readlink /proc/self/fd/0 &' </dev/tty
/dev/null
$ zsh -c 'readlink /proc/self/fd/0 &' </dev/zero
/dev/zero

Ein Workaround, der in allen Shells funktioniert, besteht darin, die Standardeingabe in einen anderen Dateideskriptor zu duplizieren und die Standardeingabe des ersten Befehls von dort umzuleiten:

#! /bin/sh
exec 3<"${1:-/dev/stdin}"
cat <&3 | timeout 30 xclip -i -selection clipboard -verbose -r >/dev/null 2>&1 &

Antwort3

dashals POSIX-Standard positioniert. POSIX spezifiziert nur [n]>die Umleitung. bashFührt aber viele eigene Features ein. &>ist eines davon und bedeutet Ausgabedeskriptoren ( stderrund stdout).

Sie sollten den Artikel lesen überBash und DashKompatibilität.

Vielleicht bekommst du hilfreichCheckbashismenbashDienstprogramm, das beim Auffinden spezifischer Anweisungen in Ihren Skripten helfen kann .

verwandte Informationen