Почему ''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).
P.S. verbose используется для того, чтобы xclip не демонизировался.

решение1

&>это башизм, вам придется изменить его на >/dev/null 2>&1для оболочек POSIX

решение2

[этот ответ посвящен асинхронным конвейерам в скриптах; для получения информации об устаревшем &>операторе bash и о том, почему вы всегда должны использовать его >output 2>&1вместо него, см.устаревший и нерекомендуемый синтаксис]

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

Здесь у вас есть конвейер, работающий асинхронно (потому что завершен &), запущенный из скрипта, т. е. из оболочки с отключенным управлением заданиями.

Согласностандартный:

command1 & [command2 & ... ]

Стандартный ввод для асинхронного списка, до выполнения каких-либо явных перенаправлений, следует считать назначенным файлу, имеющему те же свойства, что и /dev/null.

Проблема в том, что dash, ksh, mksh, yash, и т. д. интерпретируют «асинхронный список» как любую команду, включая конвейер, и перенаправляют stdin первой команды из /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интерпретирует его только как «простую команду» и перенаправляет его stdin только /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

dashпозиционируется как стандарт POSIX. POSIX определяет только [n]>перенаправление. Но bashвводит много собственных функций. &>является одним из них и означает выходные дескрипторы ( stderrи stdout).

Вам следует прочитать статью оБаш и тиресовместимость.

Может быть, вам будет полезночекбашизмыутилита, которая может помочь найти bashконкретные инструкции в ваших скриптах.

Связанный контент