shellcheck icon indicating copy to clipboard operation
shellcheck copied to clipboard

Consider suggesting `&> afile` instead of `> afile 2>&1`

Open heitorPB opened this issue 2 years ago • 4 comments

For new checks and feature suggestions

  • [x] https://www.shellcheck.net/ (i.e. the latest commit) currently gives no useful warnings about this
  • [x] I searched through https://github.com/koalaman/shellcheck/issues and didn't find anything related

Consider suggesting &> afile instead of > afile 2>&1 to redirect both stdout and stderr to the same file.

Here's a snippet or screenshot that shows the problem:


#!/usr/bin/env bash

function foo()
{
	echo -n "$1 " >&2 # send to stderr
	echo "$2"         # this goes to stdout
}

foo error out &>bla       # here both strings to to file bla

Here's what shellcheck currently says:

Nothing

Here's what I wanted or expected to see:

This might be a bit obscure (in the sense few people know about), but helps me a lot to not screw up the order of the redirects.

  • info: consider using &> afile instead of > afile 2>&1

Works on Bash and sh, did not test on other shells.

heitorPB avatar Dec 14 '23 13:12 heitorPB

That's not valid in POSIX sh, and in fact, we already have a warning to make the exact opposite change (SC3020).

See also https://unix.stackexchange.com/a/590707/217726:

&> is a synonym of >& followed by a file name in bash (and zsh). It has the advantage of working even if the file name is a sequence of digits or - (for example foo >&"$a" is equivalent to foo 1>&3 2>&1 if the value of a is the digit 3, but to foo >myfile 2>&1 if the value of a is myfile). Its downside is that it is incompatible with POSIX. In a POSIX-compliant shell, echo foo &>bar is parsed as the command echo foo, the & operator, and the command >bar: a background process that prints foo, and a foreground process that creates or empties the file bar.

And https://unix.stackexchange.com/a/159514/217726:

The &> variant originates in bash, while the >& variant comes from csh (decades earlier). They both conflict with other POSIX shell operators and should not be used in portable sh scripts.

josephcsible avatar Dec 22 '23 05:12 josephcsible

&> is a badly broken csh-ism. As @josephcsible suggests, it shouldn't be encouraged.

(ins)ormaaj 69 (1425001) 0 / $ ( eval -- set -x \; "${ tmux saveb - | tee /proc/self/fd/2; }" )
function runtest {
    unset -f "${FUNCNAME[0]}"
    local -; set +m
    typeset -ia fd
    typeset -n fdn='fd[${#fd[@]}]'

    {
        execlineb "/proc/self/fd/${fd[-4]}" "${fd[-5]}" bash "${bob[@]}" -p "/proc/self/fd/${fd[-3]}"
    } {fdn}<<-\EOF0 {fdn}<<-\EOF1 {fdn}<<-\EOF2 {fdn}<&0 {fdn}<&1 < <(typeset -p fd)
echo foo >/dev/null &>"/proc/self/fd/${1}" echo bar
EOF0
    fdreserve 1
    importas x 1
    shift
    multisubstitute {
        importas -u f FD0
        elgetpositionals
    }
    emptyenv -P
    creatememfd $f "" # https://github.com/emanuele6/emanutils
    foreground { cp -L -- /proc/self/fd/${x} /proc/self/fd/${f} }
    fdclose $x
    export BASH_COMPAT 51
    $@ $f
EOF1

function ret {
    return ${1:+"$1"}
}

function _main {
    typeset sh
    {
        sh=$(pkgconf --var=loadablesdir bash) &&
            enable -f "${sh}/fdflags" fdflags &&
            source /proc/self/fd/0 &&
            [[ -v fd && $1 == +([[:digit:]]) ]]
        } || return

        fd+=([-5]=$1)
        exec <&"${fd[-2]}"- {fd[-4]}<&-
        fdflags -s +cloexec "${fd[-3]}"
        set -- bash zsh mksh toybox bb ksh ksh93v loksh dash yash bosh jsh
        printf '\ntest: %s\n\n' "$(<"/proc/self/fd/${fd[-5]}")"

        for sh; do
            printf '%-7s ' "${sh}:"
            case $sh in
                toybox) ret 1;;
                zsh)    ret 2
            esac
            (
            exec -a ${PPID[!($?!=1)]+"$sh"}${PPID[!($?==1)]+'sh'} -- \
                "$sh" ${PPID[$?!=2]+'--emulate'} ${PPID[$?!=2]+'ksh'} "/proc/self/fd/${fd[-5]}" "${fd[-1]}"
            )
        done
    }

    _main "$@"
EOF2

    { BASH_XTRACEFD=2 set +x; } 2>/dev/null
    typeset -i x
    for x in "${!fd[@]}"; do
        exec {fd[x]}<&-
    done
}

runtest

++ runtest
++ unset -f runtest
++ local -
++ set +m
++ typeset -ia fd
++ typeset -n 'fdn=fd[${#fd[@]}]'
++ execlineb /proc/self/fd/11 10 bash -o vi +o histexpand -O lastpipe -O extglob -O expand_aliases +O array_expand_once +O assoc_expand_once -p /proc/self/fd/12
+++ typeset -p fd

test: echo foo >/dev/null &>"/proc/self/fd/${1}" echo bar

bash:   foo echo bar
zsh:    foo echo bar
mksh:   foo echo bar
toybox: foo echo bar
bb:     foo echo bar
ksh:    foo echo bar
ksh93v: bar
loksh:  bar
dash:   bar
yash:   bar
bosh:   bar
jsh:    bar

ormaaj avatar Jan 01 '24 16:01 ormaaj