Bash

為什麼 ‘‘cat ‘${1:-/dev/stdin} | … &>/dev/null’’ 在 bash 中有效,但在 dash 中無效?

  • June 7, 2019

腳本:

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

我可以看到只有標準輸入不起作用(使用 bash 它適用於標準輸入/文件)。

PS verbose 用於使 xclip 不被守護。

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

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

在這裡,您有一個非同步執行的管道(因為由 終止&),從腳本啟動,即從禁用作業控制的外殼開始。

根據標準

command1 & [command2 & ... ]

在執行任何顯式重定向之前,非同步列表的標準輸入應被視為已分配給與 具有相同屬性的文件/dev/null

問題是 , dash, ksh,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在原始標準輸入是 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 &

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

引用自:https://unix.stackexchange.com/questions/523421