¿Por qué ''cat "${1:-/dev/stdin} | ... &>/dev/null'' funciona en bash pero no en dash?

¿Por qué ''cat "${1:-/dev/stdin} | ... &>/dev/null'' funciona en bash pero no en dash?

Guion:

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

Puedo ver que solo la entrada estándar no funciona (con bash funciona en la entrada estándar/archivo).
PS detallado se utiliza para hacer que xclip no se demonice.

Respuesta1

&>es un bashismo, tendrás que cambiarlo >/dev/null 2>&1para shells POSIX

Respuesta2

[esta respuesta trata sobre canalizaciones asincrónicas en scripts; Para conocer el operador bash obsoleto &>y por qué siempre debería usarlo >output 2>&1en su lugar, consultesintaxis obsoleta y en desuso]

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

Aquí tiene una canalización que se ejecuta de forma asincrónica (porque terminada por &), iniciada desde un script, es decir, desde un shell con el control de trabajo deshabilitado.

De acuerdo con laestándar:

command1 & [command2 & ... ]

La entrada estándar para una lista asíncrona, antes de que se realicen redirecciones explícitas, se considerará asignada a un archivo que tenga las mismas propiedades que /dev/null.

El problema es que dash, ksh, mksh, yash, etc. interpretan la "lista asíncrona" como cualquier comando, incluida una canalización, y redirigirán la entrada estándar del primer comando desde /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

Pero bashsólo lo interpretará como "comando simple" y sólo redirigirá su entrada estándar /dev/nullcuando esténoparte de una tubería:

$ 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

zshsolo lo redireccionará /dev/nullcuando la entrada estándar original sea un tty, no cuando sea otro tipo de archivo:

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

Una solución alternativa que funciona en todos los shells es duplicar la entrada estándar en otro descriptor de archivo y redirigir la entrada estándar del primer comando desde allí:

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

Respuesta3

dashposicionado como estándar POSIX. POSIX solo especificó [n]>la redirección. Pero bashintroduce muchas características propias. &>es uno de ellos y significa descriptores de salida ( stderry stdout).

Deberías leer el artículo sobregolpear y corrercompatibilidad.

Tal vez te resulte útilcheckbashismosutilidad que puede ayudar a encontrar bashinstrucciones específicas en sus scripts.

información relacionada