''cat "${1:-/dev/stdin} | ... &>/dev/null'' が bash では機能するのに dash では機能しないのはなぜですか?

''cat "${1:-/dev/stdin} | ... &>/dev/null'' が bash では機能するのに dash では機能しないのはなぜですか?

脚本:

#!/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 &

stdin だけが機能しないことがわかります (bash では stdin/file で機能します)。PS
verbose は、xclip がデーモン化されないようにするために使用されます。

答え1

&>はバシズムなので、>/dev/null 2>&1POSIXシェルでは に変更する必要があります。

答え2

[この回答はスクリプト内の非同期パイプラインに関するものです。廃止された&>bash演算子と、>output 2>&1代わりに常に使用する必要がある理由については、廃止された非推奨の構文]

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

&ここでは、スクリプトから、つまりジョブ制御が無効になっているシェルから開始され、非同期で実行されるパイプラインがあります ( によって終了されたため)。

による標準:

command1 & [command2 & ... ]

明示的なリダイレクトが実行される前に、非同期リストの標準入力は、 と同じプロパティを持つファイルに割り当てられているものとみなされます/dev/null

問題はdash、、、、などkshが「非同期リスト」をパイプラインを含む任意のコマンドとして解釈し、 からの最初のコマンドの stdin をリダイレクトすることです。mkshyash/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

しかし、bashそれは「単純なコマンド」としてのみ解釈され、/dev/nullそれが実行されたときにのみ標準入力をリダイレクトします。ないパイプラインの一部:

$ 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

zsh/dev/null元の stdin が tty の場合にのみリダイレクトされ、他の種類のファイルの場合はリダイレクトされません。

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

すべてのシェルで機能する回避策は、stdin を別のファイル記述子に複製し、そこから最初のコマンドの stdin をリダイレクトすることです。

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

答え3

dashPOSIX 標準として位置付けられています。POSIX は[n]>リダイレクトのみを指定していました。しかし、bash多くの独自の機能を導入しています。&>はその 1 つで、出力記述子 (stderrおよびstdout) を意味します。

この記事を読むといい強打とダッシュ互換性。

役に立つかもしれないチェックバシズムbashスクリプト内の特定の命令を見つけるのに役立つユーティリティです。

関連情報