为什么 ''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 &

我可以看到只有标准输入不起作用(使用 bash 它可以在标准输入/文件上工作)。
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在脚本中查找特定指令。

相关内容