4

Script:

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

I can see that only stdin does not work (with bash it works on stdin/file).
P.S. verbose is used to make xclip not daemonize.

  • That won't work with /dev/stdin even with bash. Since you're running with the pipeline put in the background (terminated with &) cat /dev/stdin will either have its stdin redirected from /dev/null (when running in a script) or will be stopped by a SIGTTIN signal (when running in an interactive shell). – mosvy Jun 6 at 22:39
  • you sure about the second? because it works (bash) in my terminal echo 123 | script. Is there anyway I can do what I want to do? – droso Jun 6 at 22:49
  • Look at end of the pipeline in your Q. There's a & at the end -- the shell won't treat it as decoration ;-) – mosvy Jun 6 at 22:51
  • of course not but whole script will work in the foreground (because of -verbose but -verbose is needed to make timeout work) – droso Jun 6 at 22:58
  • Try this in your script: exec 3<"${1:-/dev/stdin}"; cat <&3 | ... &. – mosvy Jun 6 at 22:59
5

[this answer is about asynchronous pipelines in scripts; for the deprecated &> bash operator and why you should always use >output 2>&1 instead, refer to obsolete and deprecated syntax]

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

Here you have a pipeline running asynchronously (because terminated by &), started from a script, ie is from a shell with the job control disabled.

According to the standard:

command1 & [command2 & ... ]

The standard input for an asynchronous list, before any explicit redirections are performed, shall be considered to be assigned to a file that has the same properties as /dev/null.

The problem is that dash, ksh, mksh, yash, etc intepret "asynchronous list" as any command, including a pipeline, and will redirect the stdin of the first command from /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

But bash will only interpret it as "simple command" and will only redirect its stdin from /dev/null when it's not part of a pipeline:

$ 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 will only redirect it from /dev/null when the original stdin is a tty, not when it's other kind of file:

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

A workaround which works in all shells is to duplicate the stdin into another file descriptor, and redirect the stdin of the first command from it:

#! /bin/sh
exec 3<"${1:-/dev/stdin}"
cat <&3 | timeout 30 xclip -i -selection clipboard -verbose -r >/dev/null 2>&1 &
  • 1
    nitpick: in the POSIX grammar/terminology, a simple command (like readlink /proc/self/fd/0) is a pipeline (with one element) and readlink /proc/self/fd/0 | cat a pipeline with two elements. In bash and POSIX shells (but not zsh), a & affects an and-or list, not just the last pipeline in the and-or list. See also the current discussion on the austin-group ML about austingroupbugs.net/view.php?id=1254 – Stéphane Chazelas Aug 10 at 6:42
10

&> is a bashism, you will have to change it to >/dev/null 2>&1 for POSIX shells

  • 1
    Now stdout is suppressed like it should be thank you BUT stdin is still not working ($1 works). Also I had to use >/dev/null 2>&1 – droso Jun 6 at 22:22
7

dash positioned as POSIX standard. POSIX specified only [n]> redirection. But bash introduces many own features. &> is one of them and means output descriptors (stderr and stdout).

You should read article about bash and dash compatibility.

Maybe you get helpful checkbashisms utility which can helps to find bash-specific instructions in your scripts.

  • 1
    &> is only stdout and stderr, not "all" file descriptors. (It's also considered obsolescent and not suggested for use, even in bash). – Charles Duffy Jun 7 at 18:58
  • @CharlesDuffy Thank's for correction. I've just fixed it in the own answer! – Yurij Goncharuk Jun 7 at 20:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.