為什麼 ''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

&>是一個 bashism,你必須將其更改>/dev/null 2>&1為 POSIX shell

答案2

[這個答案是關於腳本中的非同步管道;對於已棄用的&>bash 運算子以及為什麼您應該始終使用>output 2>&1它,請參閱過時和不建議使用的語法]

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

這裡有一個非同步運行的管道(因為由 終止&),從腳本啟動,即從禁用作業控制的 shell 啟動。

根據標準:

command1 & [command2 & ... ]

在執行任何明確重定向之前,非同步清單的標準輸入應被視為分配給具有與/dev/null.

問題是dashkshmkshyash等將「非同步清單」解釋為任何命令,包括管道,並將重定向第一個命令的標準輸入/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只有當原始標準輸入是 tty 時才重定向它,而不是當它是其他類型的檔案時:

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

在所有 shell 中都有效的解決方法是將標準輸入複製到另一個檔案描述符中,並從中重定向第一個命令的標準輸入:

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

答案3

dash定位為 POSIX 標準。 POSIX 只指定[n]>重定向。但bash介紹了許多自己的特點。&>是其中之一,表示輸出描述符 (stderrstdout)。

你應該閱讀有關的文章重擊和破折號相容性.

也許你會得到幫助制止主義實用程式可以幫助您bash在腳本中找到特定指令。

相關內容