23 SC2155
Dan Griscom edited this page 2024-01-07 10:16:11 -05:00

Declare and assign separately to avoid masking return values.

Problematic code in the case of export:

export foo="$(mycmd)"

Correct code:

foo="$(mycmd)"
export foo

Rationale:

In the original code, the return value of mycmd is ignored, and export will instead always return true. This may prevent conditionals, set -e and traps from working correctly.

When first marked for export and assigned separately, the return value of the assignment will be that of mycmd. This avoids the problem.

Note that ShellCheck does not warn about masking of local read-only variables, such as local -r foo=$(cmd), even though this also masks the return value. This is because the alternative local foo; foo=$(cmd); local -r foo is repetitive and cumbersome. To see warnings for this and many other additional cases of suppressed exit codes, enable check-extra-masked-returns.

Exceptions:

If you intend to ignore the return value of an assignment, you can either ignore this warning or use

foo=$(mycmd) || true
export foo

Shellcheck does not warn about export foo=bar because bar is a literal and not a command substitution with an independent return value.

Problematic code in the case of local:

local foo="$(mycmd)"

Correct code:

local foo
foo=$(mycmd)

Rationale

The exit status of the command is overridden by the exit status of the creation of the local variable. For example:

$ f() { local foo=$(false) && echo "error was hidden"; }; f
error was hidden
$ f() { local foo; foo=$(false) && echo "error was hidden"; }; f

Problematic code in the case of readonly:

readonly foo="$(mycmd)"

Correct code:

foo="$(mycmd)"
readonly foo

Word splitting and quoting issue with dash, maybe others

A serious quoting problem with dash is another reason to declare and assign separately. Dash is the default, /bin/sh shell on Ubuntu. More specifically, dash version 0.5.8-2.10 and others cannot run these two examples:

f(){ local e=$1; }
f "1 2"

export g=$(printf '%s' "foo 2")

While this runs fine in other shells, dash doesn't treat any of these as assignments and fails both like this:

local: 2: bad variable name
export: 2: bad variable name

The direct workaround to this bug is to quote the right-hand-side of the assignment. Separating declaraction and assignment also makes this runs fine in any shell.

(A rule to catch this problem is in the works at #1556).