yvoronoy/m2install

View on GitHub
m2install.sh

Summary

Maintainability
Test Coverage

Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @).
Open

    if [[ "${missingDirectories[@]-}" ]]
Severity: Minor
Found in m2install.sh by shellcheck

Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @).

Problematic code:

ext=png
allowedExt=(jpg bmp png)
[[ "$ext" = "${allowedExt[@]}" ]] && echo "Extension is valid"

Correct code:

ext=png
allowedExt=(jpg bmp png)
for value in "${allowedExt[@]}"
do
  [[ "$ext" = "$value" ]] && echo "Extension is valid"
done

Rationale:

Array expansions in [[ .. ]] will implicitly concatenate into a single string, much like in assignments. The problematic code is equivalent to [ "$ext" = "jpg bmp png" ].

Instead, use a for loop to iterate over values, and apply your condition to each.

Alternatively, if you do want to concatenate all the values in the array into a single string for your test, use "$*" or "${array[*]}" to make this explicit.

Exceptions:

None.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  printString "Updating ElasticSearch Configuration $(getESConfigHost $currentSearchEngine):$(getESConfigPort $currentSearchEngine)"
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

    if $BIN_PHP $BIN_MAGE module:status Magento_LiveSearch | grep -q 'Module is enabled'
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local dbDumpFilenamePath="$(getDbDumpFilename)" 
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  exit $1
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Expressions don't expand in single quotes, use double quotes for that.
Open

  addToBootstrap 'if (count($pids) === 0) {'
Severity: Minor
Found in m2install.sh by shellcheck

Expressions don't expand in single quotes, use double quotes for that.

Problematic code:

name=World
echo 'Hello $name'

Correct code:

name=World
echo "Hello $name"

Rationale:

Single quotes prevent expansion of everything, including variables and command substitution.

If you want to use the values of variables and such, use double quotes instead.

Note that if you have other items that needs single quoting, you can use both in a single word:

echo '$1 USD is '"$rate GBP"

Exceptions

If you want $stuff to be a literal dollar sign followed by the characters "stuff", you can [[ignore]] this message.

ShellCheck tries to be smart about it, and won't warn when this is used with awk, perl and similar, but there are some inherent ambiguities like 'I have $1 in my wallet', which could be "one dollar" or "whatever's in the first parameter".

In the particular case of sed, ShellCheck uses additional heuristics to try to separate cases like 's/$foo/bar/' (failing to replace the variable $foo) with from the false positives like '$d' (delete last line). If you're still triggering these, consider being more generous with your spaces: use $ { s/foo/bar; } instead of ${s/foo/bar/;}

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Quote this to prevent word splitting.
Open

  $BIN_PHP bin/magento config:set "catalog/search/${currentSearchEngine}_server_port" $(getESConfigPort $currentSearchEngine)
Severity: Minor
Found in m2install.sh by shellcheck

Quote this to prevent word splitting

Problematic code:

ls -l $(getfilename)

Correct code:

# getfilename outputs 1 file
ls -l "$(getfilename)"

# getfilename outputs multiple files, linefeed separated
getfilename | while IFS='' read -r line
do
  ls -l "$line"
done

Rationale:

When command expansions are unquoted, word splitting and globbing will occur. This often manifests itself by breaking when filenames contain spaces.

Trying to fix it by adding quotes or escapes to the data will not work. Instead, quote the command substitution itself.

If the command substitution outputs multiple pieces of data, use a loop instead.

Exceptions

In rare cases you actually want word splitting, such as in

gcc $(pkg-config --libs openssl) client.c

This is because pkg-config outputs -lssl -lcrypto, which you want to break up by spaces into -lssl and -lcrypto. An alternative is to put the variables to an array and expand it:

args=( $(pkg-config --libs openssl) )
gcc "${args[@]}" client.c

The power of using an array becomes evident when you want to combine, for example, the command result with user-provided arguments:

compile () {
    args=( $(pkg-config --libs openssl) "${@}" )
    gcc "${args[@]}" client.c
}
compile -DDEBUG
+ gcc -lssl -lcrypto -DDEBUG client.c

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local path="$(getScriptDir)/error.csv"
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

fgrep is non-standard and deprecated. Use grep -F instead.
Open

    slashCount=$(tar -tf "$1" | grep -v vendor | fgrep pub/index.php | sed 's/pub[/]index[.]php//' | sort | head -1 | tr -cd '/' | wc -m | tr -d ' ')
Severity: Minor
Found in m2install.sh by shellcheck

fgrep is non-standard and deprecated. Use grep -F instead.

Problematic code:

fgrep '*.*' file

Correct code:

grep -F '*.*' file

Rationale:

fgrep is a non-standard command. Its functionality is provided in POSIX by grep -F. POSIX grep says:

This grep has been enhanced in an upwards-compatible way to provide the exact functionality of the historical egrep and fgrep commands as well. It was the clear intention of the standard developers to consolidate the three greps into a single command.

man grep for GNU says:

Direct invocation as either egrep or fgrep is deprecated

Exceptions:

ShellCheck will fail to recognize when functions override fgrep. Consider giving it a different name or [[ignore]] this error.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  printError $2
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Can't follow non-constant source. Use a directive to specify location.
Open

            source "$filePath"
Severity: Minor
Found in m2install.sh by shellcheck

Can't follow non-constant source. Use a directive to specify location.

Problematic code:

. "$(find_install_dir)/lib.sh"

Correct code:

# shellcheck source=src/lib.sh
. "$(find_install_dir)/lib.sh"

Rationale:

ShellCheck is not able to include sourced files from paths that are determined at runtime. The file will not be read, potentially resulting in warnings about unassigned variables and similar.

Use a [[Directive]] to point shellcheck to a fixed location it can read instead.

Exceptions:

If you don't care that ShellCheck is unable to account for the file, specify # shellcheck source=/dev/null.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Expressions don't expand in single quotes, use double quotes for that.
Open

  addToBootstrap '    exec($command . " >> /dev/null", $output, $exitCode);';
Severity: Minor
Found in m2install.sh by shellcheck

Expressions don't expand in single quotes, use double quotes for that.

Problematic code:

name=World
echo 'Hello $name'

Correct code:

name=World
echo "Hello $name"

Rationale:

Single quotes prevent expansion of everything, including variables and command substitution.

If you want to use the values of variables and such, use double quotes instead.

Note that if you have other items that needs single quoting, you can use both in a single word:

echo '$1 USD is '"$rate GBP"

Exceptions

If you want $stuff to be a literal dollar sign followed by the characters "stuff", you can [[ignore]] this message.

ShellCheck tries to be smart about it, and won't warn when this is used with awk, perl and similar, but there are some inherent ambiguities like 'I have $1 in my wallet', which could be "one dollar" or "whatever's in the first parameter".

In the particular case of sed, ShellCheck uses additional heuristics to try to separate cases like 's/$foo/bar/' (failing to replace the variable $foo) with from the false positives like '$d' (delete last line). If you're still triggering these, consider being more generous with your spaces: use $ { s/foo/bar; } instead of ${s/foo/bar/;}

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

      $BIN_PHP $BIN_MAGE module:status | grep Magento_LiveSearch | grep -v List | grep -v None | grep -v -e '^$' | xargs $BIN_PHP $BIN_MAGE module:disable
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

    echo ${user[0]} ;
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Quote this to prevent word splitting.
Open

  local stores=$(mysql -h127.0.0.1 -N -u$(getRemoteDBUser) -P${LOCAL_PORT} --execute="${SQLQUERY}")
Severity: Minor
Found in m2install.sh by shellcheck

Quote this to prevent word splitting

Problematic code:

ls -l $(getfilename)

Correct code:

# getfilename outputs 1 file
ls -l "$(getfilename)"

# getfilename outputs multiple files, linefeed separated
getfilename | while IFS='' read -r line
do
  ls -l "$line"
done

Rationale:

When command expansions are unquoted, word splitting and globbing will occur. This often manifests itself by breaking when filenames contain spaces.

Trying to fix it by adding quotes or escapes to the data will not work. Instead, quote the command substitution itself.

If the command substitution outputs multiple pieces of data, use a loop instead.

Exceptions

In rare cases you actually want word splitting, such as in

gcc $(pkg-config --libs openssl) client.c

This is because pkg-config outputs -lssl -lcrypto, which you want to break up by spaces into -lssl and -lcrypto. An alternative is to put the variables to an array and expand it:

args=( $(pkg-config --libs openssl) )
gcc "${args[@]}" client.c

The power of using an array becomes evident when you want to combine, for example, the command result with user-provided arguments:

compile () {
    args=( $(pkg-config --libs openssl) "${@}" )
    gcc "${args[@]}" client.c
}
compile -DDEBUG
+ gcc -lssl -lcrypto -DDEBUG client.c

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Expressions don't expand in single quotes, use double quotes for that.
Open

  addToBootstrap '    if ($exitCode > 0) {'
Severity: Minor
Found in m2install.sh by shellcheck

Expressions don't expand in single quotes, use double quotes for that.

Problematic code:

name=World
echo 'Hello $name'

Correct code:

name=World
echo "Hello $name"

Rationale:

Single quotes prevent expansion of everything, including variables and command substitution.

If you want to use the values of variables and such, use double quotes instead.

Note that if you have other items that needs single quoting, you can use both in a single word:

echo '$1 USD is '"$rate GBP"

Exceptions

If you want $stuff to be a literal dollar sign followed by the characters "stuff", you can [[ignore]] this message.

ShellCheck tries to be smart about it, and won't warn when this is used with awk, perl and similar, but there are some inherent ambiguities like 'I have $1 in my wallet', which could be "one dollar" or "whatever's in the first parameter".

In the particular case of sed, ShellCheck uses additional heuristics to try to separate cases like 's/$foo/bar/' (failing to replace the variable $foo) with from the false positives like '$d' (delete last line). If you're still triggering these, consider being more generous with your spaces: use $ { s/foo/bar; } instead of ${s/foo/bar/;}

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Argument mixes string and array. Use * or separate argument.
Open

    >&2 echo "ERROR: $@";
Severity: Minor
Found in m2install.sh by shellcheck

Argument mixes string and array. Use * or separate argument.

Problematic code:

printf "Error: %s\n" "Bad parameters: $@"

Correct code:

printf "Error: %s\n" "Bad parameters: $*"

Rationale:

The behavior when concatenating a string and array is rarely intended. The preceeding string is prefixed to the first array element, while the succeeding string is appended to the last one. The middle array elements are unaffected.

E.g., with the parameters foo,bar,baz, "--flag=$@" is equivalent to the three arguments "--flag=foo" "bar" "baz".

If the intention is to concatenate all the array elements into one argument, use $*. This concatenates based on IFS.

If the intention is to provide each array element as a separate argument, put the array expansion in its own argument.

Exceptions

None.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local dbDumpFileSize="$(wc -c ${dbDumpFilenamePath} | awk '{print $1}')"
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

es_engine appears unused. Verify it or export it.
Open

    local es_engine es_host es_port es_prefix elasticsuite version versions=("" "5" "6" "7")
Severity: Minor
Found in m2install.sh by shellcheck

foo appears unused. Verify it or export it.

Problematic code:

foo=42
echo "$FOO"

Correct code:

foo=42
echo "$foo"

Rationale:

Variables not used for anything are often associated with bugs, so ShellCheck warns about them.

Also note that something like local let foo=42 does not make a let statement local -- it instead declares an additional local variable named let.

Exceptions

ShellCheck may not always realize that the variable is in use (especially with indirection), and may not realize you don't care (with throwaway variables or unimplemented features).

For throwaway variables, consider using _ as a dummy:

read _ last _ zip _ _ <<< "$str"
echo "$last, $zip"

or use a directive to disable the warning:

# shellcheck disable=SC2034
read first last email zip lat lng <<< "$str"
echo "$last, $zip"

For indirection, there's not much you can do without rewriting to use arrays or similar:

bar=42  # will always appear unused
foo=bar
echo "${!foo}"

This is expected behavior, and not a bug. There is no good way to statically analyze indirection in shell scripts, just like static C analyzers have a hard time preventing segfaults.

As always, there are ways to [[ignore]] this and other messages if they frequently get in your way.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

    local deployConfigurator=$(cat << EOF
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  $BIN_PHP bin/magento config:set "catalog/search/${currentSearchEngine}_server_hostname" $(getESConfigHost $currentSearchEngine)
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Argument to -z is always false due to literal strings.
Open

    if [ -z getDbDumpFilename ]
Severity: Minor
Found in m2install.sh by shellcheck

Argument to implicit -n is always true due to literal strings.

Problematic code:

if [ "$foo " ]
then
  echo "this is always true"
fi

Correct code:

if [ "$foo" ]
then
  echo "correctly checks value"
fi

Rationale:

Since [ str ] checks that the string is non-empty, the space inside the quotes in the problematic code causes the test to always be true, since a string with a space can not be empty.

Sometimes this is also caused by overquoting an example, e.g. [ "$foo -gt 0" ], which is always true for the same reason. The intention here was [ "$foo" -gt 0 ].

Exceptions:

None.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local codeDumpFilenamePath="$(getCodeDumpFilename)" 
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Quote this to prevent word splitting.
Open

  if [ -z "$(getAllTables \"$(getTablePrefix)store\")" ]
Severity: Minor
Found in m2install.sh by shellcheck

Quote this to prevent word splitting

Problematic code:

ls -l $(getfilename)

Correct code:

# getfilename outputs 1 file
ls -l "$(getfilename)"

# getfilename outputs multiple files, linefeed separated
getfilename | while IFS='' read -r line
do
  ls -l "$line"
done

Rationale:

When command expansions are unquoted, word splitting and globbing will occur. This often manifests itself by breaking when filenames contain spaces.

Trying to fix it by adding quotes or escapes to the data will not work. Instead, quote the command substitution itself.

If the command substitution outputs multiple pieces of data, use a loop instead.

Exceptions

In rare cases you actually want word splitting, such as in

gcc $(pkg-config --libs openssl) client.c

This is because pkg-config outputs -lssl -lcrypto, which you want to break up by spaces into -lssl and -lcrypto. An alternative is to put the variables to an array and expand it:

args=( $(pkg-config --libs openssl) )
gcc "${args[@]}" client.c

The power of using an array becomes evident when you want to combine, for example, the command result with user-provided arguments:

compile () {
    args=( $(pkg-config --libs openssl) "${@}" )
    gcc "${args[@]}" client.c
}
compile -DDEBUG
+ gcc -lssl -lcrypto -DDEBUG client.c

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Quote this to prevent word splitting.
Open

  $BIN_PHP bin/magento config:set "catalog/search/${currentSearchEngine}_server_hostname" $(getESConfigHost $currentSearchEngine)
Severity: Minor
Found in m2install.sh by shellcheck

Quote this to prevent word splitting

Problematic code:

ls -l $(getfilename)

Correct code:

# getfilename outputs 1 file
ls -l "$(getfilename)"

# getfilename outputs multiple files, linefeed separated
getfilename | while IFS='' read -r line
do
  ls -l "$line"
done

Rationale:

When command expansions are unquoted, word splitting and globbing will occur. This often manifests itself by breaking when filenames contain spaces.

Trying to fix it by adding quotes or escapes to the data will not work. Instead, quote the command substitution itself.

If the command substitution outputs multiple pieces of data, use a loop instead.

Exceptions

In rare cases you actually want word splitting, such as in

gcc $(pkg-config --libs openssl) client.c

This is because pkg-config outputs -lssl -lcrypto, which you want to break up by spaces into -lssl and -lcrypto. An alternative is to put the variables to an array and expand it:

args=( $(pkg-config --libs openssl) )
gcc "${args[@]}" client.c

The power of using an array becomes evident when you want to combine, for example, the command result with user-provided arguments:

compile () {
    args=( $(pkg-config --libs openssl) "${@}" )
    gcc "${args[@]}" client.c
}
compile -DDEBUG
+ gcc -lssl -lcrypto -DDEBUG client.c

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  echo "$(date '+%Y-%m-%d %H:%M:%S'), $1, $(whoami), $(pwd), $BASH_SOURCE, \"$GLOBAL_ARGS\"" >> $errorLogFile
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  $BIN_PHP $BIN_MAGE module:status | grep -E '.*ServicesId*|.*ServicesConnector*|.*DataExporter*|.*SaaS*|.*DataServices*'| xargs $BIN_PHP $BIN_MAGE  module:enable
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local path="$(getScriptDir)/m2install.csv"
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Argument mixes string and array. Use * or separate argument.
Open

        echo "The following files are missing: ${missingFiles[@]}";
Severity: Minor
Found in m2install.sh by shellcheck

Argument mixes string and array. Use * or separate argument.

Problematic code:

printf "Error: %s\n" "Bad parameters: $@"

Correct code:

printf "Error: %s\n" "Bad parameters: $*"

Rationale:

The behavior when concatenating a string and array is rarely intended. The preceeding string is prefixed to the first array element, while the succeeding string is appended to the last one. The middle array elements are unaffected.

E.g., with the parameters foo,bar,baz, "--flag=$@" is equivalent to the three arguments "--flag=foo" "bar" "baz".

If the intention is to concatenate all the array elements into one argument, use $*. This concatenates based on IFS.

If the intention is to provide each array element as a separate argument, put the array expansion in its own argument.

Exceptions

None.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  $BIN_PHP bin/magento config:set "catalog/search/${currentSearchEngine}_server_port" $(getESConfigPort $currentSearchEngine)
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Expanding an array without an index only gives the first element.
Open

  echo "$(date '+%Y-%m-%d %H:%M:%S'), $1, $(whoami), $(pwd), $BASH_SOURCE, \"$GLOBAL_ARGS\"" >> $errorLogFile
Severity: Minor
Found in m2install.sh by shellcheck

Expanding an array without an index only gives the first element.

Problematic code:

myarray=(foo bar)
for f in $myarray
do
  cat "$f"
done

Correct code:

myarray=(foo bar)
for f in "${myarray[@]}"
do
  cat "$f"
done

Rationale:

When referencing arrays, $myarray is equivalent to ${myarray[0]} -- it results in only the first of multiple elements.

To get all elements as separate parameters, use the index @ (and make sure to double quote). In the example, echo "${myarray[@]}" is equivalent to echo "foo" "bar".

To get all elements as a single parameter, concatenated by the first character in IFS, use the index *. In the example, echo "${myarray[*]}" is equivalent to echo "foo bar".

Exceptions

None.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

    local dir="$( cd -P "$( dirname "$source" )" && pwd )"
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local stores=$(mysql -h127.0.0.1 -N -u$(getRemoteDBUser) -P${LOCAL_PORT} --execute="${SQLQUERY}")
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Expressions don't expand in single quotes, use double quotes for that.
Open

  addToBootstrap 'file_put_contents("kill_tunnel.sh", PHP_EOL . "kill " . implode(" ", $pids));'
Severity: Minor
Found in m2install.sh by shellcheck

Expressions don't expand in single quotes, use double quotes for that.

Problematic code:

name=World
echo 'Hello $name'

Correct code:

name=World
echo "Hello $name"

Rationale:

Single quotes prevent expansion of everything, including variables and command substitution.

If you want to use the values of variables and such, use double quotes instead.

Note that if you have other items that needs single quoting, you can use both in a single word:

echo '$1 USD is '"$rate GBP"

Exceptions

If you want $stuff to be a literal dollar sign followed by the characters "stuff", you can [[ignore]] this message.

ShellCheck tries to be smart about it, and won't warn when this is used with awk, perl and similar, but there are some inherent ambiguities like 'I have $1 in my wallet', which could be "one dollar" or "whatever's in the first parameter".

In the particular case of sed, ShellCheck uses additional heuristics to try to separate cases like 's/$foo/bar/' (failing to replace the variable $foo) with from the false positives like '$d' (delete last line). If you're still triggering these, consider being more generous with your spaces: use $ { s/foo/bar; } instead of ${s/foo/bar/;}

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

      $BIN_PHP $BIN_MAGE module:status | grep Magento_LiveSearch | grep -v List | grep -v None | grep -v -e '^$' | xargs $BIN_PHP $BIN_MAGE module:disable
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

    BASE_URL=$(echo ${BASE_URL} | sed "s/\/\/$/\//g" )
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Quote this to prevent word splitting.
Open

  if ! tar -tf $(getCodeDumpFilename) | grep '^index.php'
Severity: Minor
Found in m2install.sh by shellcheck

Quote this to prevent word splitting

Problematic code:

ls -l $(getfilename)

Correct code:

# getfilename outputs 1 file
ls -l "$(getfilename)"

# getfilename outputs multiple files, linefeed separated
getfilename | while IFS='' read -r line
do
  ls -l "$line"
done

Rationale:

When command expansions are unquoted, word splitting and globbing will occur. This often manifests itself by breaking when filenames contain spaces.

Trying to fix it by adding quotes or escapes to the data will not work. Instead, quote the command substitution itself.

If the command substitution outputs multiple pieces of data, use a loop instead.

Exceptions

In rare cases you actually want word splitting, such as in

gcc $(pkg-config --libs openssl) client.c

This is because pkg-config outputs -lssl -lcrypto, which you want to break up by spaces into -lssl and -lcrypto. An alternative is to put the variables to an array and expand it:

args=( $(pkg-config --libs openssl) )
gcc "${args[@]}" client.c

The power of using an array becomes evident when you want to combine, for example, the command result with user-provided arguments:

compile () {
    args=( $(pkg-config --libs openssl) "${@}" )
    gcc "${args[@]}" client.c
}
compile -DDEBUG
+ gcc -lssl -lcrypto -DDEBUG client.c

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Argument mixes string and array. Use * or separate argument.
Open

        echo "The following directories are missing: ${missingDirectories[@]}";
Severity: Minor
Found in m2install.sh by shellcheck

Argument mixes string and array. Use * or separate argument.

Problematic code:

printf "Error: %s\n" "Bad parameters: $@"

Correct code:

printf "Error: %s\n" "Bad parameters: $*"

Rationale:

The behavior when concatenating a string and array is rarely intended. The preceeding string is prefixed to the first array element, while the succeeding string is appended to the last one. The middle array elements are unaffected.

E.g., with the parameters foo,bar,baz, "--flag=$@" is equivalent to the three arguments "--flag=foo" "bar" "baz".

If the intention is to concatenate all the array elements into one argument, use $*. This concatenates based on IFS.

If the intention is to provide each array element as a separate argument, put the array expansion in its own argument.

Exceptions

None.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

      $BIN_PHP $BIN_MAGE --quiet config:set  'catalog/search/engine' $(getRecommendedSearchEngineForVersion)
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Assigning an array to a string! Assign as array, or use * instead of @ to concatenate.
Open

GLOBAL_ARGS="$@"
Severity: Minor
Found in m2install.sh by shellcheck

Assigning an array to a string! Assign as array, or use * instead of @ to concatenate.

Problematic code:

var=$@
for i in $var; do ..; done

or

set -- Hello World
msg=$@
echo "You said $msg"

Correct code:

var=( "$@" )
for i in "${var[@]}"; do ..; done

or

set -- Hello World
msg=$*
echo "You said $msg"

Rationale:

Arrays and $@ can contain multiple elements. Simple variables contain only one. When assigning multiple elements to one element, the default behavior depends on the shell (bash concatenates with spaces, zsh concatenates with first char of IFS).

Since doing this usually indicates a bug, ShellCheck warns and asks you to be explicit about what you want.

If you want to assign N elements as N elements, use an array, e.g. myArray=( "$@" ).

If you want to assign N elements as 1 element by concatenating them, use * instead of @, e.g. myVar=${myArray[*]} (this separates elements with the first character of IFS, usually space).

The same is true for ${@: -1}, which results in 0 or 1 elements: var=${*: -1} assigns the last element or an empty string.

Exceptions

None.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

                CMD="tar $(getStripComponentsValue ${EXTRACT_FILENAME}) -xf ${EXTRACT_FILENAME} $1"
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local codeDumpFileSize="$(wc -c ${codeDumpFilenamePath} | awk '{print $1}')"
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Expressions don't expand in single quotes, use double quotes for that.
Open

  addToBootstrap '        throw new \Exception("Remote Host ${REMOTE_HOST} is unavailable, check your network settings or VPN connection");'
Severity: Minor
Found in m2install.sh by shellcheck

Expressions don't expand in single quotes, use double quotes for that.

Problematic code:

name=World
echo 'Hello $name'

Correct code:

name=World
echo "Hello $name"

Rationale:

Single quotes prevent expansion of everything, including variables and command substitution.

If you want to use the values of variables and such, use double quotes instead.

Note that if you have other items that needs single quoting, you can use both in a single word:

echo '$1 USD is '"$rate GBP"

Exceptions

If you want $stuff to be a literal dollar sign followed by the characters "stuff", you can [[ignore]] this message.

ShellCheck tries to be smart about it, and won't warn when this is used with awk, perl and similar, but there are some inherent ambiguities like 'I have $1 in my wallet', which could be "one dollar" or "whatever's in the first parameter".

In the particular case of sed, ShellCheck uses additional heuristics to try to separate cases like 's/$foo/bar/' (failing to replace the variable $foo) with from the false positives like '$d' (delete last line). If you're still triggering these, consider being more generous with your spaces: use $ { s/foo/bar; } instead of ${s/foo/bar/;}

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @).
Open

    if [[ "${missingDirectories[@]-}" || "${missingFiles[@]-}" ]]
Severity: Minor
Found in m2install.sh by shellcheck

Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @).

Problematic code:

ext=png
allowedExt=(jpg bmp png)
[[ "$ext" = "${allowedExt[@]}" ]] && echo "Extension is valid"

Correct code:

ext=png
allowedExt=(jpg bmp png)
for value in "${allowedExt[@]}"
do
  [[ "$ext" = "$value" ]] && echo "Extension is valid"
done

Rationale:

Array expansions in [[ .. ]] will implicitly concatenate into a single string, much like in assignments. The problematic code is equivalent to [ "$ext" = "jpg bmp png" ].

Instead, use a for loop to iterate over values, and apply your condition to each.

Alternatively, if you do want to concatenate all the values in the array into a single string for your test, use "$*" or "${array[*]}" to make this explicit.

Exceptions:

None.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @).
Open

    if [[ "${missingDirectories[@]-}" || "${missingFiles[@]-}" ]]
Severity: Minor
Found in m2install.sh by shellcheck

Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @).

Problematic code:

ext=png
allowedExt=(jpg bmp png)
[[ "$ext" = "${allowedExt[@]}" ]] && echo "Extension is valid"

Correct code:

ext=png
allowedExt=(jpg bmp png)
for value in "${allowedExt[@]}"
do
  [[ "$ext" = "$value" ]] && echo "Extension is valid"
done

Rationale:

Array expansions in [[ .. ]] will implicitly concatenate into a single string, much like in assignments. The problematic code is equivalent to [ "$ext" = "jpg bmp png" ].

Instead, use a for loop to iterate over values, and apply your condition to each.

Alternatively, if you do want to concatenate all the values in the array into a single string for your test, use "$*" or "${array[*]}" to make this explicit.

Exceptions:

None.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  $BIN_PHP $BIN_MAGE module:status | grep -E 'Magento_Elasticsearch*|Magento_AdvancedSearch|Magento_InventoryElasticsearch' | grep -v List | grep -v None | grep -v -e '^$' | xargs $BIN_PHP $BIN_MAGE module:disable
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Quote this to prevent word splitting.
Open

    echo $(grep 'table_prefix' app/etc/env.php | head -n1 | sed "s/[a-z'_ ]*[=][>][ ]*[']//" | sed "s/['][,]*//")
Severity: Minor
Found in m2install.sh by shellcheck

Quote this to prevent word splitting

Problematic code:

ls -l $(getfilename)

Correct code:

# getfilename outputs 1 file
ls -l "$(getfilename)"

# getfilename outputs multiple files, linefeed separated
getfilename | while IFS='' read -r line
do
  ls -l "$line"
done

Rationale:

When command expansions are unquoted, word splitting and globbing will occur. This often manifests itself by breaking when filenames contain spaces.

Trying to fix it by adding quotes or escapes to the data will not work. Instead, quote the command substitution itself.

If the command substitution outputs multiple pieces of data, use a loop instead.

Exceptions

In rare cases you actually want word splitting, such as in

gcc $(pkg-config --libs openssl) client.c

This is because pkg-config outputs -lssl -lcrypto, which you want to break up by spaces into -lssl and -lcrypto. An alternative is to put the variables to an array and expand it:

args=( $(pkg-config --libs openssl) )
gcc "${args[@]}" client.c

The power of using an array becomes evident when you want to combine, for example, the command result with user-provided arguments:

compile () {
    args=( $(pkg-config --libs openssl) "${@}" )
    gcc "${args[@]}" client.c
}
compile -DDEBUG
+ gcc -lssl -lcrypto -DDEBUG client.c

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  $BIN_PHP $BIN_MAGE module:status | grep -E 'Magento_Elasticsearch*|Magento_AdvancedSearch|Magento_InventoryElasticsearch' | grep -v List | grep -v None | grep -v -e '^$' | xargs $BIN_PHP $BIN_MAGE module:disable
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  $BIN_PHP $BIN_MAGE module:status | grep Magento_LiveSearch | grep -v List | grep -v None | grep -v -e '^$' | xargs $BIN_PHP $BIN_MAGE module:enable
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

        CMD="${CMD} --search-engine=$searchEngine --elasticsearch-host=$(getESConfigHost $searchEngine) --elasticsearch-port=$(getESConfigPort $searchEngine) --elasticsearch-index-prefix=${DB_NAME}"
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local csvFile="$(getCsvLogFile)"
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Argument to -z is always false due to literal strings.
Open

    if [ -z getCodeDumpFilename ]
Severity: Minor
Found in m2install.sh by shellcheck

Argument to implicit -n is always true due to literal strings.

Problematic code:

if [ "$foo " ]
then
  echo "this is always true"
fi

Correct code:

if [ "$foo" ]
then
  echo "correctly checks value"
fi

Rationale:

Since [ str ] checks that the string is non-empty, the space inside the quotes in the problematic code causes the test to always be true, since a string with a space can not be empty.

Sometimes this is also caused by overquoting an example, e.g. [ "$foo -gt 0" ], which is always true for the same reason. The intention here was [ "$foo" -gt 0 ].

Exceptions:

None.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  local dbDumpFileSize="$(wc -c ${dbDumpFilenamePath} | awk '{print $1}')"
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

    local recursiveconfigs=$( (find "$(pwd)" -maxdepth 1 -name "${CONFIG_NAME}" ;\
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Expressions don't expand in single quotes, use double quotes for that.
Open

  addToBootstrap 'exec("ps aux | grep -v \" grep\" | grep \"$command\" | tr -s \" \" | cut -d \" \" -f 2", $pids);'
Severity: Minor
Found in m2install.sh by shellcheck

Expressions don't expand in single quotes, use double quotes for that.

Problematic code:

name=World
echo 'Hello $name'

Correct code:

name=World
echo "Hello $name"

Rationale:

Single quotes prevent expansion of everything, including variables and command substitution.

If you want to use the values of variables and such, use double quotes instead.

Note that if you have other items that needs single quoting, you can use both in a single word:

echo '$1 USD is '"$rate GBP"

Exceptions

If you want $stuff to be a literal dollar sign followed by the characters "stuff", you can [[ignore]] this message.

ShellCheck tries to be smart about it, and won't warn when this is used with awk, perl and similar, but there are some inherent ambiguities like 'I have $1 in my wallet', which could be "one dollar" or "whatever's in the first parameter".

In the particular case of sed, ShellCheck uses additional heuristics to try to separate cases like 's/$foo/bar/' (failing to replace the variable $foo) with from the false positives like '$d' (delete last line). If you're still triggering these, consider being more generous with your spaces: use $ { s/foo/bar; } instead of ${s/foo/bar/;}

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Expressions don't expand in single quotes, use double quotes for that.
Open

  addToBootstrap '    exec("ps aux | grep -v \" grep\" | grep \"$command\" | tr -s \" \" | cut -d \" \" -f 2", $pids);'
Severity: Minor
Found in m2install.sh by shellcheck

Expressions don't expand in single quotes, use double quotes for that.

Problematic code:

name=World
echo 'Hello $name'

Correct code:

name=World
echo "Hello $name"

Rationale:

Single quotes prevent expansion of everything, including variables and command substitution.

If you want to use the values of variables and such, use double quotes instead.

Note that if you have other items that needs single quoting, you can use both in a single word:

echo '$1 USD is '"$rate GBP"

Exceptions

If you want $stuff to be a literal dollar sign followed by the characters "stuff", you can [[ignore]] this message.

ShellCheck tries to be smart about it, and won't warn when this is used with awk, perl and similar, but there are some inherent ambiguities like 'I have $1 in my wallet', which could be "one dollar" or "whatever's in the first parameter".

In the particular case of sed, ShellCheck uses additional heuristics to try to separate cases like 's/$foo/bar/' (failing to replace the variable $foo) with from the false positives like '$d' (delete last line). If you're still triggering these, consider being more generous with your spaces: use $ { s/foo/bar; } instead of ${s/foo/bar/;}

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @).
Open

    if [[ "${missingFiles[@]-}" ]]
Severity: Minor
Found in m2install.sh by shellcheck

Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @).

Problematic code:

ext=png
allowedExt=(jpg bmp png)
[[ "$ext" = "${allowedExt[@]}" ]] && echo "Extension is valid"

Correct code:

ext=png
allowedExt=(jpg bmp png)
for value in "${allowedExt[@]}"
do
  [[ "$ext" = "$value" ]] && echo "Extension is valid"
done

Rationale:

Array expansions in [[ .. ]] will implicitly concatenate into a single string, much like in assignments. The problematic code is equivalent to [ "$ext" = "jpg bmp png" ].

Instead, use a for loop to iterate over values, and apply your condition to each.

Alternatively, if you do want to concatenate all the values in the array into a single string for your test, use "$*" or "${array[*]}" to make this explicit.

Exceptions:

None.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local currentSearchEngine="$($BIN_PHP bin/magento config:show catalog/search/engine)"
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  $BIN_PHP bin/magento config:set "catalog/search/${currentSearchEngine}_index_prefix" $DB_NAME
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

isElasticSearchConfigIsAvailable references arguments, but none are ever passed.
Open

function isElasticSearchConfigIsAvailable()
Severity: Minor
Found in m2install.sh by shellcheck

foo references arguments, but none are ever passed.

Problematic code:

sayhello() {
  echo "Hello $1"
}
sayhello

./myscript World just prints "Hello " instead of "Hello World".

Correct code:

sayhello() {
  echo "Hello $1"
}
sayhello "$@"

./myscript World now prints "Hello World".

Rationale:

In a function, $1 and up refers to the function's parameters, not the script's parameters.

If you want to process your script's parameters in a function, you have to explicitly pass them. You can do this with myfunction "$@".

Note that "$@" refers to the current context's positional parameters, so if you call a function from a function, you have to pass in "$@" to both of them:

first() { second "$@"; }
second() { echo "The first script parameter is: $1"; }
first "$@"

Exceptions

If the parameters are optional and you currently just don't want to use them, you can ignore this message.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Can't follow non-constant source. Use a directive to specify location.
Open

        source "$1";
Severity: Minor
Found in m2install.sh by shellcheck

Can't follow non-constant source. Use a directive to specify location.

Problematic code:

. "$(find_install_dir)/lib.sh"

Correct code:

# shellcheck source=src/lib.sh
. "$(find_install_dir)/lib.sh"

Rationale:

ShellCheck is not able to include sourced files from paths that are determined at runtime. The file will not be read, potentially resulting in warnings about unassigned variables and similar.

Use a [[Directive]] to point shellcheck to a fixed location it can read instead.

Exceptions:

If you don't care that ShellCheck is unable to account for the file, specify # shellcheck source=/dev/null.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  local admin_response_code="$(curl --insecure --location --write-out '%{http_code}' --silent --output /dev/null $admin_url)"
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Expressions don't expand in single quotes, use double quotes for that.
Open

  local codeLine='$_SERVER[\\Magento\\Store\\Model\\StoreManager::PARAM_RUN_CODE] = '"'${websiteCode}';";
Severity: Minor
Found in m2install.sh by shellcheck

Expressions don't expand in single quotes, use double quotes for that.

Problematic code:

name=World
echo 'Hello $name'

Correct code:

name=World
echo "Hello $name"

Rationale:

Single quotes prevent expansion of everything, including variables and command substitution.

If you want to use the values of variables and such, use double quotes instead.

Note that if you have other items that needs single quoting, you can use both in a single word:

echo '$1 USD is '"$rate GBP"

Exceptions

If you want $stuff to be a literal dollar sign followed by the characters "stuff", you can [[ignore]] this message.

ShellCheck tries to be smart about it, and won't warn when this is used with awk, perl and similar, but there are some inherent ambiguities like 'I have $1 in my wallet', which could be "one dollar" or "whatever's in the first parameter".

In the particular case of sed, ShellCheck uses additional heuristics to try to separate cases like 's/$foo/bar/' (failing to replace the variable $foo) with from the false positives like '$d' (delete last line). If you're still triggering these, consider being more generous with your spaces: use $ { s/foo/bar; } instead of ${s/foo/bar/;}

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

      $BIN_PHP $BIN_MAGE module:status | grep Magento_LiveSearch | grep -v List | grep -v None | grep -v -e '^$' | xargs $BIN_PHP $BIN_MAGE module:disable
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Note that unlike globs, h* here matches 'hhh' but not 'hotel'.
Open

      $BIN_PHP $BIN_MAGE module:status | grep -E 'Magento_Elasticsearch*|Magento_AdvancedSearch|Magento_InventoryElasticsearch' | grep -v List | grep -v None | grep -v -e '^$' | xargs $BIN_PHP $BIN_MAGE module:enable
Severity: Minor
Found in m2install.sh by shellcheck

Note that unlike globs, o* here matches 'ooo' but not 'oscar'

Problematic code:

grep 'foo*'

when wanting to match food and foosball, but not mofo or keyfob.

Correct code:

grep '^foo'

Rationale:

As a glob, foo* means "Any string starting with foo", e.g. food and foosball.

As a regular expression, "foo*" means "f followed by 1 or more o's, anywhere", e.g. "mofo" or "keyfob".

This construct is way more common as a glob than as a regex, so ShellCheck notifies you about it.

Exceptions

If you're aware of the above, you can ignore this message. If you'd like shellcheck to be quiet, use a [[directive]] or 'fo[o]*'.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

      $BIN_PHP $BIN_MAGE module:status | grep -E 'Magento_Elasticsearch*|Magento_AdvancedSearch|Magento_InventoryElasticsearch' | grep -v List | grep -v None | grep -v -e '^$' | xargs $BIN_PHP $BIN_MAGE module:enable
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  $BIN_PHP $BIN_MAGE module:status | grep -E 'Magento_Elasticsearch*|Magento_AdvancedSearch|Magento_InventoryElasticsearch' | grep -v List | grep -v None | grep -v -e '^$' | xargs $BIN_PHP $BIN_MAGE module:disable
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  $BIN_PHP $BIN_MAGE --quiet config:set  'catalog/search/engine' NULL
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local dir="$(pwd)"
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

    $BIN_PHP $BIN_MAGE module:status $moduleName | grep -q 'Module is enabled' && $BIN_PHP $BIN_MAGE module:disable $moduleName && echo "$moduleName is being disabled"
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local green=`tput setaf 2`
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

          $BIN_PHP bin/magento --quiet config:set "web/upward/path" ${ABSOLUTE_PATH}/${PWA}/upward.yml
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

        local searchEngine="$(getRecommendedSearchEngineForVersion)"
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Expanding an array without an index only gives the first element.
Open

  local currentScript="$BASH_SOURCE"
Severity: Minor
Found in m2install.sh by shellcheck

Expanding an array without an index only gives the first element.

Problematic code:

myarray=(foo bar)
for f in $myarray
do
  cat "$f"
done

Correct code:

myarray=(foo bar)
for f in "${myarray[@]}"
do
  cat "$f"
done

Rationale:

When referencing arrays, $myarray is equivalent to ${myarray[0]} -- it results in only the first of multiple elements.

To get all elements as separate parameters, use the index @ (and make sure to double quote). In the example, echo "${myarray[@]}" is equivalent to echo "foo" "bar".

To get all elements as a single parameter, concatenated by the first character in IFS, use the index *. In the example, echo "${myarray[*]}" is equivalent to echo "foo bar".

Exceptions

None.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local currentDirectory="$(pwd)"
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Quote this to prevent word splitting.
Open

  [ ! -L "`pwd`/${websiteDir}/pub" ] && ln -s `pwd`/pub "`pwd`/${websiteDir}/pub"
Severity: Minor
Found in m2install.sh by shellcheck

Quote this to prevent word splitting

Problematic code:

ls -l $(getfilename)

Correct code:

# getfilename outputs 1 file
ls -l "$(getfilename)"

# getfilename outputs multiple files, linefeed separated
getfilename | while IFS='' read -r line
do
  ls -l "$line"
done

Rationale:

When command expansions are unquoted, word splitting and globbing will occur. This often manifests itself by breaking when filenames contain spaces.

Trying to fix it by adding quotes or escapes to the data will not work. Instead, quote the command substitution itself.

If the command substitution outputs multiple pieces of data, use a loop instead.

Exceptions

In rare cases you actually want word splitting, such as in

gcc $(pkg-config --libs openssl) client.c

This is because pkg-config outputs -lssl -lcrypto, which you want to break up by spaces into -lssl and -lcrypto. An alternative is to put the variables to an array and expand it:

args=( $(pkg-config --libs openssl) )
gcc "${args[@]}" client.c

The power of using an array becomes evident when you want to combine, for example, the command result with user-provided arguments:

compile () {
    args=( $(pkg-config --libs openssl) "${@}" )
    gcc "${args[@]}" client.c
}
compile -DDEBUG
+ gcc -lssl -lcrypto -DDEBUG client.c

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local errorLogFile="$(getErrorLogFile)"
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Argument mixes string and array. Use * or separate argument.
Open

    echo "${configPaths[@]} "
Severity: Minor
Found in m2install.sh by shellcheck

Argument mixes string and array. Use * or separate argument.

Problematic code:

printf "Error: %s\n" "Bad parameters: $@"

Correct code:

printf "Error: %s\n" "Bad parameters: $*"

Rationale:

The behavior when concatenating a string and array is rarely intended. The preceeding string is prefixed to the first array element, while the succeeding string is appended to the last one. The middle array elements are unaffected.

E.g., with the parameters foo,bar,baz, "--flag=$@" is equivalent to the three arguments "--flag=foo" "bar" "baz".

If the intention is to concatenate all the array elements into one argument, use $*. This concatenates based on IFS.

If the intention is to provide each array element as a separate argument, put the array expansion in its own argument.

Exceptions

None.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

      $BIN_PHP $BIN_MAGE module:status | grep -E 'Magento_Elasticsearch*|Magento_AdvancedSearch|Magento_InventoryElasticsearch' | grep -v List | grep -v None | grep -v -e '^$' | xargs $BIN_PHP $BIN_MAGE module:enable
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

red appears unused. Verify it or export it.
Open

  local red=`tput setaf 1`
Severity: Minor
Found in m2install.sh by shellcheck

foo appears unused. Verify it or export it.

Problematic code:

foo=42
echo "$FOO"

Correct code:

foo=42
echo "$foo"

Rationale:

Variables not used for anything are often associated with bugs, so ShellCheck warns about them.

Also note that something like local let foo=42 does not make a let statement local -- it instead declares an additional local variable named let.

Exceptions

ShellCheck may not always realize that the variable is in use (especially with indirection), and may not realize you don't care (with throwaway variables or unimplemented features).

For throwaway variables, consider using _ as a dummy:

read _ last _ zip _ _ <<< "$str"
echo "$last, $zip"

or use a directive to disable the warning:

# shellcheck disable=SC2034
read first last email zip lat lng <<< "$str"
echo "$last, $zip"

For indirection, there's not much you can do without rewriting to use arrays or similar:

bar=42  # will always appear unused
foo=bar
echo "${!foo}"

This is expected behavior, and not a bug. There is no good way to statically analyze indirection in shell scripts, just like static C analyzers have a hard time preventing segfaults.

As always, there are ways to [[ignore]] this and other messages if they frequently get in your way.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local engine=$(getConfig 'catalog/search/engine' "value");
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Note that unlike globs, h* here matches 'hhh' but not 'hotel'.
Open

  $BIN_PHP $BIN_MAGE module:status | grep -E 'Magento_Elasticsearch*|Magento_AdvancedSearch|Magento_InventoryElasticsearch' | grep -v List | grep -v None | grep -v -e '^$' | xargs $BIN_PHP $BIN_MAGE module:disable
Severity: Minor
Found in m2install.sh by shellcheck

Note that unlike globs, o* here matches 'ooo' but not 'oscar'

Problematic code:

grep 'foo*'

when wanting to match food and foosball, but not mofo or keyfob.

Correct code:

grep '^foo'

Rationale:

As a glob, foo* means "Any string starting with foo", e.g. food and foosball.

As a regular expression, "foo*" means "f followed by 1 or more o's, anywhere", e.g. "mofo" or "keyfob".

This construct is way more common as a glob than as a regex, so ShellCheck notifies you about it.

Exceptions

If you're aware of the above, you can ignore this message. If you'd like shellcheck to be quiet, use a [[directive]] or 'fo[o]*'.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local elasticSearchVersion=$(curl -s -X GET "$eshost:$esport" | grep number | sed 's/[^0-9.]//g' | head -c 1)
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local admin_response_code="$(curl --insecure --location --write-out '%{http_code}' --silent --output /dev/null $admin_url)"
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  $BIN_PHP $BIN_MAGE module:status | grep Magento_LiveSearch | grep -v List | grep -v None | grep -v -e '^$' | xargs $BIN_PHP $BIN_MAGE module:enable
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

    $BIN_PHP $BIN_MAGE module:status $moduleName | grep -q 'Module is enabled' && $BIN_PHP $BIN_MAGE module:disable $moduleName && echo "$moduleName is being disabled"
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local yellow=`tput setaf 3`
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

    if [ $MAGENTO_MAJOR_VERSION -ge 4 ] && [ $MAGENTO_MINOR_VERSION -ge 1 ]
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local esport=$(getESConfigPort "$searchEngine")
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

    askValue "Git branch" ${MAGENTO_VERSION}
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

    local isBranchExists=$(${BIN_GIT} ls-remote ${repoName} | grep -F ${versionName})
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local result=$(parseMagentoVersion "Magento CLI 2.2.4-p10")
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @).
Open

    if [[ "${STEPS[@]}" ]]
Severity: Minor
Found in m2install.sh by shellcheck

Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @).

Problematic code:

ext=png
allowedExt=(jpg bmp png)
[[ "$ext" = "${allowedExt[@]}" ]] && echo "Extension is valid"

Correct code:

ext=png
allowedExt=(jpg bmp png)
for value in "${allowedExt[@]}"
do
  [[ "$ext" = "$value" ]] && echo "Extension is valid"
done

Rationale:

Array expansions in [[ .. ]] will implicitly concatenate into a single string, much like in assignments. The problematic code is equivalent to [ "$ext" = "jpg bmp png" ].

Instead, use a for loop to iterate over values, and apply your condition to each.

Alternatively, if you do want to concatenate all the values in the array into a single string for your test, use "$*" or "${array[*]}" to make this explicit.

Exceptions:

None.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local default=`tput sgr0`
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  if [ ! -z $2 ]
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

    if [ $MAGENTO_MAJOR_VERSION -ge 4 ] && [ $MAGENTO_MINOR_VERSION -ge 1 ]
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

        CMD="${CMD} --search-engine=$searchEngine --elasticsearch-host=$(getESConfigHost $searchEngine) --elasticsearch-port=$(getESConfigPort $searchEngine) --elasticsearch-index-prefix=${DB_NAME}"
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local eshost=$(getESConfigHost "$searchEngine")
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Quote this to prevent word splitting.
Open

      searchEngine="$(parseElasticSearchVersion $(getESConfigHost) $(getESConfigPort))"
Severity: Minor
Found in m2install.sh by shellcheck

Quote this to prevent word splitting

Problematic code:

ls -l $(getfilename)

Correct code:

# getfilename outputs 1 file
ls -l "$(getfilename)"

# getfilename outputs multiple files, linefeed separated
getfilename | while IFS='' read -r line
do
  ls -l "$line"
done

Rationale:

When command expansions are unquoted, word splitting and globbing will occur. This often manifests itself by breaking when filenames contain spaces.

Trying to fix it by adding quotes or escapes to the data will not work. Instead, quote the command substitution itself.

If the command substitution outputs multiple pieces of data, use a loop instead.

Exceptions

In rare cases you actually want word splitting, such as in

gcc $(pkg-config --libs openssl) client.c

This is because pkg-config outputs -lssl -lcrypto, which you want to break up by spaces into -lssl and -lcrypto. An alternative is to put the variables to an array and expand it:

args=( $(pkg-config --libs openssl) )
gcc "${args[@]}" client.c

The power of using an array becomes evident when you want to combine, for example, the command result with user-provided arguments:

compile () {
    args=( $(pkg-config --libs openssl) "${@}" )
    gcc "${args[@]}" client.c
}
compile -DDEBUG
+ gcc -lssl -lcrypto -DDEBUG client.c

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local worktreeCEPath="$(getWorktreePath CE)"
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

    local isBranchExists=$(${BIN_GIT} ls-remote ${repoName} | grep -F ${versionName})
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Quote this to prevent word splitting.
Open

      $BIN_PHP $BIN_MAGE --quiet config:set  'catalog/search/engine' $(getRecommendedSearchEngineForVersion)
Severity: Minor
Found in m2install.sh by shellcheck

Quote this to prevent word splitting

Problematic code:

ls -l $(getfilename)

Correct code:

# getfilename outputs 1 file
ls -l "$(getfilename)"

# getfilename outputs multiple files, linefeed separated
getfilename | while IFS='' read -r line
do
  ls -l "$line"
done

Rationale:

When command expansions are unquoted, word splitting and globbing will occur. This often manifests itself by breaking when filenames contain spaces.

Trying to fix it by adding quotes or escapes to the data will not work. Instead, quote the command substitution itself.

If the command substitution outputs multiple pieces of data, use a loop instead.

Exceptions

In rare cases you actually want word splitting, such as in

gcc $(pkg-config --libs openssl) client.c

This is because pkg-config outputs -lssl -lcrypto, which you want to break up by spaces into -lssl and -lcrypto. An alternative is to put the variables to an array and expand it:

args=( $(pkg-config --libs openssl) )
gcc "${args[@]}" client.c

The power of using an array becomes evident when you want to combine, for example, the command result with user-provided arguments:

compile () {
    args=( $(pkg-config --libs openssl) "${@}" )
    gcc "${args[@]}" client.c
}
compile -DDEBUG
+ gcc -lssl -lcrypto -DDEBUG client.c

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

green appears unused. Verify it or export it.
Open

  local green=`tput setaf 2`
Severity: Minor
Found in m2install.sh by shellcheck

foo appears unused. Verify it or export it.

Problematic code:

foo=42
echo "$FOO"

Correct code:

foo=42
echo "$foo"

Rationale:

Variables not used for anything are often associated with bugs, so ShellCheck warns about them.

Also note that something like local let foo=42 does not make a let statement local -- it instead declares an additional local variable named let.

Exceptions

ShellCheck may not always realize that the variable is in use (especially with indirection), and may not realize you don't care (with throwaway variables or unimplemented features).

For throwaway variables, consider using _ as a dummy:

read _ last _ zip _ _ <<< "$str"
echo "$last, $zip"

or use a directive to disable the warning:

# shellcheck disable=SC2034
read first last email zip lat lng <<< "$str"
echo "$last, $zip"

For indirection, there's not much you can do without rewriting to use arrays or similar:

bar=42  # will always appear unused
foo=bar
echo "${!foo}"

This is expected behavior, and not a bug. There is no good way to statically analyze indirection in shell scripts, just like static C analyzers have a hard time preventing segfaults.

As always, there are ways to [[ignore]] this and other messages if they frequently get in your way.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

    local deployConfigurator=$(cat << EOF
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  if [ -z ${!configName} ]
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

    $BIN_PHP $BIN_MAGE module:status $moduleName | grep -q 'Module is enabled' && $BIN_PHP $BIN_MAGE module:disable $moduleName && echo "$moduleName is being disabled"
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Use 'cd ... || exit' or 'cd ... || return' in case cd fails.
Open

  local homeDirectory="$(cd ~; pwd)"
Severity: Minor
Found in m2install.sh by shellcheck

Use cd ... || exit in case cd fails.

Problematic code:

cd generated_files
rm -r *.c

func(){
  cd foo
  do_something
}

Correct code:

cd generated_files || exit
rm -r *.c

# For functions, you may want to use return:
func(){
  cd foo || return
  do_something
}

Rationale:

cd can fail for a variety of reasons: misspelled paths, missing directories, missing permissions, broken symlinks and more.

If/when it does, the script will keep going and do all its operations in the wrong directory. This can be messy, especially if the operations involve creating or deleting a lot of files.

To avoid this, make sure you handle the cases when cd fails. Ways to do this include

  • cd foo || exit as suggested to just abort immediately
  • if cd foo; then echo "Ok"; else echo "Fail"; fi for custom handling
  • <(cd foo && cmd) as an alternative to <(cd foo || exit; cmd) in <(..), $(..) or ( )

Exceptions:

ShellCheck does not give this warning when cd is on the left of a || or &&, or the condition of a if, while or until loop. Having a set -e command anywhere in the script will disable this message, even though it won't necessarily prevent the issue.

If you are accounting for cd failures in a way shellcheck doesn't realize, you can disable this message with a [[directive]].

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local esRequired=$(php -r "echo (version_compare('$mageVersion', '$defaultVersion') >= 0) ? 'REQUIRED' : 'NO';")
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Quote this to prevent word splitting.
Open

      searchEngine="$(parseElasticSearchVersion $(getESConfigHost) $(getESConfigPort))"
Severity: Minor
Found in m2install.sh by shellcheck

Quote this to prevent word splitting

Problematic code:

ls -l $(getfilename)

Correct code:

# getfilename outputs 1 file
ls -l "$(getfilename)"

# getfilename outputs multiple files, linefeed separated
getfilename | while IFS='' read -r line
do
  ls -l "$line"
done

Rationale:

When command expansions are unquoted, word splitting and globbing will occur. This often manifests itself by breaking when filenames contain spaces.

Trying to fix it by adding quotes or escapes to the data will not work. Instead, quote the command substitution itself.

If the command substitution outputs multiple pieces of data, use a loop instead.

Exceptions

In rare cases you actually want word splitting, such as in

gcc $(pkg-config --libs openssl) client.c

This is because pkg-config outputs -lssl -lcrypto, which you want to break up by spaces into -lssl and -lcrypto. An alternative is to put the variables to an array and expand it:

args=( $(pkg-config --libs openssl) )
gcc "${args[@]}" client.c

The power of using an array becomes evident when you want to combine, for example, the command result with user-provided arguments:

compile () {
    args=( $(pkg-config --libs openssl) "${@}" )
    gcc "${args[@]}" client.c
}
compile -DDEBUG
+ gcc -lssl -lcrypto -DDEBUG client.c

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Use 'cd ... || exit' or 'cd ... || return' in case cd fails.
Open

    cd "$currentDir"
Severity: Minor
Found in m2install.sh by shellcheck

Use cd ... || exit in case cd fails.

Problematic code:

cd generated_files
rm -r *.c

func(){
  cd foo
  do_something
}

Correct code:

cd generated_files || exit
rm -r *.c

# For functions, you may want to use return:
func(){
  cd foo || return
  do_something
}

Rationale:

cd can fail for a variety of reasons: misspelled paths, missing directories, missing permissions, broken symlinks and more.

If/when it does, the script will keep going and do all its operations in the wrong directory. This can be messy, especially if the operations involve creating or deleting a lot of files.

To avoid this, make sure you handle the cases when cd fails. Ways to do this include

  • cd foo || exit as suggested to just abort immediately
  • if cd foo; then echo "Ok"; else echo "Fail"; fi for custom handling
  • <(cd foo && cmd) as an alternative to <(cd foo || exit; cmd) in <(..), $(..) or ( )

Exceptions:

ShellCheck does not give this warning when cd is on the left of a || or &&, or the condition of a if, while or until loop. Having a set -e command anywhere in the script will disable this message, even though it won't necessarily prevent the issue.

If you are accounting for cd failures in a way shellcheck doesn't realize, you can disable this message with a [[directive]].

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local homeDirectory="$(cd ~; pwd)"
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local result=$(parseMagentoVersion "Magento CLI 2.3.4")
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

      $BIN_PHP $BIN_MAGE module:status | grep -E 'Magento_Elasticsearch*|Magento_AdvancedSearch|Magento_InventoryElasticsearch' | grep -v List | grep -v None | grep -v -e '^$' | xargs $BIN_PHP $BIN_MAGE module:enable
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  $BIN_PHP $BIN_MAGE module:status | grep Magento_LiveSearch | grep -v List | grep -v None | grep -v -e '^$' | xargs $BIN_PHP $BIN_MAGE module:enable
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

The surrounding quotes actually unquote this. Remove or escape them.
Open

        CMD="[ ! -d "$B2B_VERSION" ] && ${BIN_GIT} clone --branch ${B2B_VERSION} --single-branch ${GIT_B2B_REPO} ${GIT_B2B_PATH}"
Severity: Minor
Found in m2install.sh by shellcheck

The surrounding quotes actually unquote this. Remove or escape them.

Problematic code:

echo "You enter "$HOSTNAME". You can smell the wumpus." >> /etc/issue

Correct code:

echo "You enter $HOSTNAME. You can smell the wumpus." >> /etc/issue

Rationale:

Always quoting variables and command expansions is good practice, but blindly putting quotes left and right of them is not.

In this case, ShellCheck has noticed that the quotes around the expansion are unquoting it, because the left quote is terminating an existing double quoted string, while the right quote starts a new one:

echo "You enter "$HOSTNAME". You can smell the wumpus."
     |----------|         |---------------------------|
        Quoted   No quotes           Quoted

If the quotes were supposed to be literal, they should be escaped. If the quotes were supposed to quote an expansion (as in the example), they should be removed because this is already a double quoted string.

Exceptions

None.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

currentMagentoVersion appears unused. Verify it or export it.
Open

    local currentMagentoVersion="$(getMagentoVersion)"
Severity: Minor
Found in m2install.sh by shellcheck

foo appears unused. Verify it or export it.

Problematic code:

foo=42
echo "$FOO"

Correct code:

foo=42
echo "$foo"

Rationale:

Variables not used for anything are often associated with bugs, so ShellCheck warns about them.

Also note that something like local let foo=42 does not make a let statement local -- it instead declares an additional local variable named let.

Exceptions

ShellCheck may not always realize that the variable is in use (especially with indirection), and may not realize you don't care (with throwaway variables or unimplemented features).

For throwaway variables, consider using _ as a dummy:

read _ last _ zip _ _ <<< "$str"
echo "$last, $zip"

or use a directive to disable the warning:

# shellcheck disable=SC2034
read first last email zip lat lng <<< "$str"
echo "$last, $zip"

For indirection, there's not much you can do without rewriting to use arrays or similar:

bar=42  # will always appear unused
foo=bar
echo "${!foo}"

This is expected behavior, and not a bug. There is no good way to statically analyze indirection in shell scripts, just like static C analyzers have a hard time preventing segfaults.

As always, there are ways to [[ignore]] this and other messages if they frequently get in your way.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local currentDir=$(pwd);
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Use 'cd ... || exit' or 'cd ... || return' in case cd fails.
Open

    cd "$worktreeEEPath"
Severity: Minor
Found in m2install.sh by shellcheck

Use cd ... || exit in case cd fails.

Problematic code:

cd generated_files
rm -r *.c

func(){
  cd foo
  do_something
}

Correct code:

cd generated_files || exit
rm -r *.c

# For functions, you may want to use return:
func(){
  cd foo || return
  do_something
}

Rationale:

cd can fail for a variety of reasons: misspelled paths, missing directories, missing permissions, broken symlinks and more.

If/when it does, the script will keep going and do all its operations in the wrong directory. This can be messy, especially if the operations involve creating or deleting a lot of files.

To avoid this, make sure you handle the cases when cd fails. Ways to do this include

  • cd foo || exit as suggested to just abort immediately
  • if cd foo; then echo "Ok"; else echo "Fail"; fi for custom handling
  • <(cd foo && cmd) as an alternative to <(cd foo || exit; cmd) in <(..), $(..) or ( )

Exceptions:

ShellCheck does not give this warning when cd is on the left of a || or &&, or the condition of a if, while or until loop. Having a set -e command anywhere in the script will disable this message, even though it won't necessarily prevent the issue.

If you are accounting for cd failures in a way shellcheck doesn't realize, you can disable this message with a [[directive]].

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Quote this to prevent word splitting.
Open

  [[ ! "$ELASTICSEARCH_HOST" ]] && ELASTICSEARCH_HOST="$(getESConfigHost $(getRecommendedSearchEngineForVersion))"
Severity: Minor
Found in m2install.sh by shellcheck

Quote this to prevent word splitting

Problematic code:

ls -l $(getfilename)

Correct code:

# getfilename outputs 1 file
ls -l "$(getfilename)"

# getfilename outputs multiple files, linefeed separated
getfilename | while IFS='' read -r line
do
  ls -l "$line"
done

Rationale:

When command expansions are unquoted, word splitting and globbing will occur. This often manifests itself by breaking when filenames contain spaces.

Trying to fix it by adding quotes or escapes to the data will not work. Instead, quote the command substitution itself.

If the command substitution outputs multiple pieces of data, use a loop instead.

Exceptions

In rare cases you actually want word splitting, such as in

gcc $(pkg-config --libs openssl) client.c

This is because pkg-config outputs -lssl -lcrypto, which you want to break up by spaces into -lssl and -lcrypto. An alternative is to put the variables to an array and expand it:

args=( $(pkg-config --libs openssl) )
gcc "${args[@]}" client.c

The power of using an array becomes evident when you want to combine, for example, the command result with user-provided arguments:

compile () {
    args=( $(pkg-config --libs openssl) "${@}" )
    gcc "${args[@]}" client.c
}
compile -DDEBUG
+ gcc -lssl -lcrypto -DDEBUG client.c

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Quote this to prevent word splitting.
Open

  [[ ! "$ELASTICSEARCH_PORT" ]] && ELASTICSEARCH_PORT="$(getESConfigPort $(getRecommendedSearchEngineForVersion))"
Severity: Minor
Found in m2install.sh by shellcheck

Quote this to prevent word splitting

Problematic code:

ls -l $(getfilename)

Correct code:

# getfilename outputs 1 file
ls -l "$(getfilename)"

# getfilename outputs multiple files, linefeed separated
getfilename | while IFS='' read -r line
do
  ls -l "$line"
done

Rationale:

When command expansions are unquoted, word splitting and globbing will occur. This often manifests itself by breaking when filenames contain spaces.

Trying to fix it by adding quotes or escapes to the data will not work. Instead, quote the command substitution itself.

If the command substitution outputs multiple pieces of data, use a loop instead.

Exceptions

In rare cases you actually want word splitting, such as in

gcc $(pkg-config --libs openssl) client.c

This is because pkg-config outputs -lssl -lcrypto, which you want to break up by spaces into -lssl and -lcrypto. An alternative is to put the variables to an array and expand it:

args=( $(pkg-config --libs openssl) )
gcc "${args[@]}" client.c

The power of using an array becomes evident when you want to combine, for example, the command result with user-provided arguments:

compile () {
    args=( $(pkg-config --libs openssl) "${@}" )
    gcc "${args[@]}" client.c
}
compile -DDEBUG
+ gcc -lssl -lcrypto -DDEBUG client.c

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  echo "$@" >> $csvFile
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  printString "Updating ElasticSearch Configuration $(getESConfigHost $currentSearchEngine):$(getESConfigPort $currentSearchEngine)"
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

    local currentMagentoVersion="$(getMagentoVersion)"
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

    local worktreeEEPath="$(getWorktreePath EE)"
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

    $BIN_PHP $BIN_MAGE module:status $moduleName | grep -q 'Module is enabled' && $BIN_PHP $BIN_MAGE module:disable $moduleName && echo "$moduleName is being disabled"
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Quote this to prevent word splitting.
Open

    loadConfigFile $(getConfigFiles)
Severity: Minor
Found in m2install.sh by shellcheck

Quote this to prevent word splitting

Problematic code:

ls -l $(getfilename)

Correct code:

# getfilename outputs 1 file
ls -l "$(getfilename)"

# getfilename outputs multiple files, linefeed separated
getfilename | while IFS='' read -r line
do
  ls -l "$line"
done

Rationale:

When command expansions are unquoted, word splitting and globbing will occur. This often manifests itself by breaking when filenames contain spaces.

Trying to fix it by adding quotes or escapes to the data will not work. Instead, quote the command substitution itself.

If the command substitution outputs multiple pieces of data, use a loop instead.

Exceptions

In rare cases you actually want word splitting, such as in

gcc $(pkg-config --libs openssl) client.c

This is because pkg-config outputs -lssl -lcrypto, which you want to break up by spaces into -lssl and -lcrypto. An alternative is to put the variables to an array and expand it:

args=( $(pkg-config --libs openssl) )
gcc "${args[@]}" client.c

The power of using an array becomes evident when you want to combine, for example, the command result with user-provided arguments:

compile () {
    args=( $(pkg-config --libs openssl) "${@}" )
    gcc "${args[@]}" client.c
}
compile -DDEBUG
+ gcc -lssl -lcrypto -DDEBUG client.c

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  local codeDumpFileSize="$(wc -c ${codeDumpFilenamePath} | awk '{print $1}')"
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

     eval $ssh_command >> /dev/null
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local red=`tput setaf 1`
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

          $BIN_PHP bin/magento --quiet config:set "web/upward/path" ${ABSOLUTE_PATH}/${PWA}/upward.yml
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

        ORIGIN_URL=$(grep -o 'data-media-backend=\"https\?://[^/]\+/' ${PWA}/index.html | grep -o 'https\?://[^/]\+/')
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

        echo $ORIGIN_URL
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  $BIN_PHP $BIN_MAGE module:status | grep -E '.*ServicesId*|.*ServicesConnector*|.*DataExporter*|.*SaaS*|.*DataServices*'| xargs $BIN_PHP $BIN_MAGE  module:enable
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Use isElasticSearchConfigIsAvailable "$@" if function's $1 should mean script's $1.
Open

    if isElasticSearchRequired && isElasticSearchConfigIsAvailable
Severity: Minor
Found in m2install.sh by shellcheck

Use foo "$@" if function's $1 should mean script's $1.

See companion warning [[SC2120]].

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Use 'cd ... || exit' or 'cd ... || return' in case cd fails.
Open

  cd "$currentDir"
Severity: Minor
Found in m2install.sh by shellcheck

Use cd ... || exit in case cd fails.

Problematic code:

cd generated_files
rm -r *.c

func(){
  cd foo
  do_something
}

Correct code:

cd generated_files || exit
rm -r *.c

# For functions, you may want to use return:
func(){
  cd foo || return
  do_something
}

Rationale:

cd can fail for a variety of reasons: misspelled paths, missing directories, missing permissions, broken symlinks and more.

If/when it does, the script will keep going and do all its operations in the wrong directory. This can be messy, especially if the operations involve creating or deleting a lot of files.

To avoid this, make sure you handle the cases when cd fails. Ways to do this include

  • cd foo || exit as suggested to just abort immediately
  • if cd foo; then echo "Ok"; else echo "Fail"; fi for custom handling
  • <(cd foo && cmd) as an alternative to <(cd foo || exit; cmd) in <(..), $(..) or ( )

Exceptions:

ShellCheck does not give this warning when cd is on the left of a || or &&, or the condition of a if, while or until loop. Having a set -e command anywhere in the script will disable this message, even though it won't necessarily prevent the issue.

If you are accounting for cd failures in a way shellcheck doesn't realize, you can disable this message with a [[directive]].

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local home_response_code="$(curl --insecure --location --write-out '%{http_code}' --silent --output /dev/null $home_url)"
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local currentUser="$(whoami)"
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  echo "<li><a href=\"${websiteCode}/\">${websiteDir}</a> (Store ID: $(getWebsiteIdByCode ${websiteCode}))</li>" >> websites/index.php
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Expressions don't expand in single quotes, use double quotes for that.
Open

  local typeLine='$_SERVER[\\Magento\\Store\\Model\\StoreManager::PARAM_RUN_TYPE] = '"'website';";
Severity: Minor
Found in m2install.sh by shellcheck

Expressions don't expand in single quotes, use double quotes for that.

Problematic code:

name=World
echo 'Hello $name'

Correct code:

name=World
echo "Hello $name"

Rationale:

Single quotes prevent expansion of everything, including variables and command substitution.

If you want to use the values of variables and such, use double quotes instead.

Note that if you have other items that needs single quoting, you can use both in a single word:

echo '$1 USD is '"$rate GBP"

Exceptions

If you want $stuff to be a literal dollar sign followed by the characters "stuff", you can [[ignore]] this message.

ShellCheck tries to be smart about it, and won't warn when this is used with awk, perl and similar, but there are some inherent ambiguities like 'I have $1 in my wallet', which could be "one dollar" or "whatever's in the first parameter".

In the particular case of sed, ShellCheck uses additional heuristics to try to separate cases like 's/$foo/bar/' (failing to replace the variable $foo) with from the false positives like '$d' (delete last line). If you're still triggering these, consider being more generous with your spaces: use $ { s/foo/bar; } instead of ${s/foo/bar/;}

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local result=$(parseMagentoVersion "Magento CLI 2.4.4")
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

        local ABSOLUTE_PATH="$(pwd)"
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  $BIN_PHP $BIN_MAGE module:status | grep -E '.*ServicesId*|.*ServicesConnector*|.*DataExporter*|.*SaaS*|.*DataServices*'| xargs $BIN_PHP $BIN_MAGE  module:enable
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Use 'cd ... || exit' or 'cd ... || return' in case cd fails.
Open

  cd "$worktreeCEPath"
Severity: Minor
Found in m2install.sh by shellcheck

Use cd ... || exit in case cd fails.

Problematic code:

cd generated_files
rm -r *.c

func(){
  cd foo
  do_something
}

Correct code:

cd generated_files || exit
rm -r *.c

# For functions, you may want to use return:
func(){
  cd foo || return
  do_something
}

Rationale:

cd can fail for a variety of reasons: misspelled paths, missing directories, missing permissions, broken symlinks and more.

If/when it does, the script will keep going and do all its operations in the wrong directory. This can be messy, especially if the operations involve creating or deleting a lot of files.

To avoid this, make sure you handle the cases when cd fails. Ways to do this include

  • cd foo || exit as suggested to just abort immediately
  • if cd foo; then echo "Ok"; else echo "Fail"; fi for custom handling
  • <(cd foo && cmd) as an alternative to <(cd foo || exit; cmd) in <(..), $(..) or ( )

Exceptions:

ShellCheck does not give this warning when cd is on the left of a || or &&, or the condition of a if, while or until loop. Having a set -e command anywhere in the script will disable this message, even though it won't necessarily prevent the issue.

If you are accounting for cd failures in a way shellcheck doesn't realize, you can disable this message with a [[directive]].

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

    local isBranchExists=$(${BIN_GIT} ls-remote ${repoName} | grep -F ${versionName})
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Double quote to prevent globbing and word splitting.
Open

  local home_response_code="$(curl --insecure --location --write-out '%{http_code}' --silent --output /dev/null $home_url)"
Severity: Minor
Found in m2install.sh by shellcheck

Double quote to prevent globbing and word splitting.

Problematic code:

echo $1
for i in $*; do :; done # this done and the next one also applies to expanding arrays.
for i in $@; do :; done

Correct code:

echo "$1"
for i in "$@"; do :; done # or, 'for i; do'

Rationale

The first code looks like "print the first argument". It's actually "Split the first argument by IFS (spaces, tabs and line feeds). Expand each of them as if it was a glob. Join all the resulting strings and filenames with spaces. Print the result."

The second one looks like "iterate through all arguments". It's actually "join all the arguments by the first character of IFS (space), split them by IFS and expand each of them as globs, and iterate on the resulting list". The third one skips the joining part.

Quoting variables prevents word splitting and glob expansion, and prevents the script from breaking when input contains spaces, line feeds, glob characters and such.

Strictly speaking, only expansions themselves need to be quoted, but for stylistic reasons, entire arguments with multiple variable and literal parts are often quoted as one:

$HOME/$dir/dist/bin/$file        # Unquoted (bad)
"$HOME"/"$dir"/dist/bin/"$file"  # Minimal quoting (good)
"$HOME/$dir/dist/bin/$file"      # Canonical quoting (good)

When quoting composite arguments, make sure to exclude globs and brace expansions, which lose their special meaning in double quotes: "$HOME/$dir/src/*.c" will not expand, but "$HOME/$dir/src"/*.c will.

Note that $( ) starts a new context, and variables in it have to be quoted independently:

echo "This $variable is quoted $(but this $variable is not)"
echo "This $variable is quoted $(and now this "$variable" is too)"

Exceptions

Sometimes you want to split on spaces, like when building a command line:

options="-j 5 -B"
make $options file

Just quoting this doesn't work. Instead, you should have used an array (bash, ksh, zsh):

options=(-j 5 -B) # ksh: set -A options -- -j 5 -B
make "${options[@]}" file

or a function (POSIX):

make_with_flags() { make -j 5 -B "$@"; }
make_with_flags file

To split on spaces but not perform glob expansion, Posix has a set -f to disable globbing. You can disable word splitting by setting IFS=''.

Similarly, you might want an optional argument:

debug=""
[[ $1 == "--trace-commands" ]] && debug="-x"
bash $debug script

Quoting this doesn't work, since in the default case, "$debug" would expand to one empty argument while $debug would expand into zero arguments. In this case, you can use an array with zero or one elements as outlined above, or you can use an unquoted expansion with an alternate value:

debug=""
[[ $1 == "--trace-commands" ]] && debug="yes"
bash ${debug:+"-x"} script

This is better than an unquoted value because the alternative value can be properly quoted, e.g. wget ${output:+ -o "$output"}.


As always, this warning can be [[ignore]]d on a case-by-case basis.

this is especially relevant when BASH many not be available for the array work around. For example, use in eval or in command options where script has total control of the variables...

FLAGS="-av -e 'ssh -x' --delete --delete-excluded"
...
# shellcheck disable=SC2086
eval rsync $FLAGS ~/dir remote_host:dir

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Declare and assign separately to avoid masking return values.
Open

  local result=$(parseMagentoVersion "Magento CLI 2.4.4-p1")
Severity: Minor
Found in m2install.sh by shellcheck

Declare and assign separately to avoid masking return values.

Problematic code:

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.

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. It also does not warn about local -r foo=$(cmd), where declaration and assignment must be in the same command.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Use $(..) instead of legacy ...
Open

  local default=`tput sgr0`
Severity: Minor
Found in m2install.sh by shellcheck

Use $(STATEMENT) instead of legacy `STATEMENT`

Problematic code

echo "Current time: `date`"

Correct code

echo "Current time: $(date)"

Rationale

Backtick command substitution `STATEMENT` is legacy syntax with several issues.

  1. It has a series of undefined behaviors related to quoting in POSIX.
  2. It imposes a custom escaping mode with surprising results.
  3. It's exceptionally hard to nest.

$(STATEMENT) command substitution has none of these problems, and is therefore strongly encouraged.

Exceptions

None.

See also

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Useless echo? Instead of 'echo $(cmd)', just use 'cmd'.
Open

    echo "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )";
Severity: Minor
Found in m2install.sh by shellcheck

Useless echo? Instead of echo $(cmd), just use cmd

Problematic code:

echo "$(cat 1.txt)"
echo `< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c6`

Correct code:

cat 1.txt # In bash, but faster and still sticks exactly one newline: printf '%s\n' "$(<1.txt)"
# The original `echo` sticks a newline; we want it too.
< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c6; echo

Rationale

The command substitution $(foo) yields the result of command foo with trailing newlines erased, and when it is passed to echo it generally just gives the same result as foo.

Exceptions

One may want to use command substitutions plus echo to make sure there is exactly one trailing newline. The special command substitution $(<file) in bash is also un-outline-able.

Anyway, echo is still not that reliable (see [[SC2039#echo-flags]]) and printf should be used instead.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Use $(..) instead of legacy ...
Open

        B2B_VERSION_MINOR=$(( `echo "${MAGENTO_MINOR_VERSION}"` -1 ))
Severity: Minor
Found in m2install.sh by shellcheck

Use $(STATEMENT) instead of legacy `STATEMENT`

Problematic code

echo "Current time: `date`"

Correct code

echo "Current time: $(date)"

Rationale

Backtick command substitution `STATEMENT` is legacy syntax with several issues.

  1. It has a series of undefined behaviors related to quoting in POSIX.
  2. It imposes a custom escaping mode with surprising results.
  3. It's exceptionally hard to nest.

$(STATEMENT) command substitution has none of these problems, and is therefore strongly encouraged.

Exceptions

None.

See also

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Useless echo? Instead of 'cmd $(echo foo)', just use 'cmd foo'.
Open

        B2B_VERSION_MINOR=$(( `echo "${MAGENTO_MINOR_VERSION}"` -1 ))
Severity: Minor
Found in m2install.sh by shellcheck

SC2116 Useless echo? Instead of 'cmd $(echo foo)', just use 'cmd foo'.

Problematic code:

greeting=$(echo "Hello, $name")
# or
tar czf "$(echo "$(date +%F).tar.gz")" *

Correct code:

greeting="Hello, $name"
# or
tar czf "$(date +%F).tar.gz" *

Rationale:

You appear to be using echo to write a value to stdout, and then using $(..) or `..` to capture the value again. This is as pointless as mailing yourself a postcard: you already have what you want, so there's no need to send it on a round trip.

You can just replace $(echo myvalue) with myvalue.

Exceptions

Sometimes this pattern is used because of side effect of echo or expansions. For example, here $(echo ..) is used to expand a glob. glob="*.png" files="$(echo $var)"

The echo is not useless, but this code is problematic because it concatenates filenames by spaces. This will break filenames containing spaces and other characters later when the list is split again. Better options are:

  • Arrays, if supported by the shell: files=( $glob ); echo "The first file is ${files[0]}"
  • Positional parameters when possible: set -- $glob; echo "The first file is $1"
  • Delaying expansion until it's needed: for file in $glob; do ...

All three methods will let you avoid issues with special characters in filenames.

As another example, here $(echo ..) is used to expand escape sequences: unexpanded='var\tvalue' expanded="$(echo "$var")"

In this case, use printf instead. It's well defined with regard to escape sequences.

Finally, if you really do want to concatenate a series of elements by a character like space, consider doing it explicitly with for or printf (e.g. printf '%s\n' $glob).

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Use $(..) instead of legacy ...
Open

  [ ! -L "`pwd`/${websiteDir}/pub" ] && ln -s `pwd`/pub "`pwd`/${websiteDir}/pub"
Severity: Minor
Found in m2install.sh by shellcheck

Use $(STATEMENT) instead of legacy `STATEMENT`

Problematic code

echo "Current time: `date`"

Correct code

echo "Current time: $(date)"

Rationale

Backtick command substitution `STATEMENT` is legacy syntax with several issues.

  1. It has a series of undefined behaviors related to quoting in POSIX.
  2. It imposes a custom escaping mode with surprising results.
  3. It's exceptionally hard to nest.

$(STATEMENT) command substitution has none of these problems, and is therefore strongly encouraged.

Exceptions

None.

See also

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Useless echo? Instead of 'echo $(cmd)', just use 'cmd'.
Open

    echo $(grep 'table_prefix' app/etc/env.php | head -n1 | sed "s/[a-z'_ ]*[=][>][ ]*[']//" | sed "s/['][,]*//")
Severity: Minor
Found in m2install.sh by shellcheck

Useless echo? Instead of echo $(cmd), just use cmd

Problematic code:

echo "$(cat 1.txt)"
echo `< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c6`

Correct code:

cat 1.txt # In bash, but faster and still sticks exactly one newline: printf '%s\n' "$(<1.txt)"
# The original `echo` sticks a newline; we want it too.
< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c6; echo

Rationale

The command substitution $(foo) yields the result of command foo with trailing newlines erased, and when it is passed to echo it generally just gives the same result as foo.

Exceptions

One may want to use command substitutions plus echo to make sure there is exactly one trailing newline. The special command substitution $(<file) in bash is also un-outline-able.

Anyway, echo is still not that reliable (see [[SC2039#echo-flags]]) and printf should be used instead.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Use $(..) instead of legacy ...
Open

    MAGENTO_MINOR_VERSION=`echo "${REAL_MAGENTO_VERSION}" | sed 's/.*2\.\([0-9]*\)\.\([0-9]*\).*/\2/'`
Severity: Minor
Found in m2install.sh by shellcheck

Use $(STATEMENT) instead of legacy `STATEMENT`

Problematic code

echo "Current time: `date`"

Correct code

echo "Current time: $(date)"

Rationale

Backtick command substitution `STATEMENT` is legacy syntax with several issues.

  1. It has a series of undefined behaviors related to quoting in POSIX.
  2. It imposes a custom escaping mode with surprising results.
  3. It's exceptionally hard to nest.

$(STATEMENT) command substitution has none of these problems, and is therefore strongly encouraged.

Exceptions

None.

See also

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Useless echo? Instead of 'cmd $(echo foo)', just use 'cmd foo'.
Open

        B2B_VERSION_MAJOR=$(( `echo "${MAGENTO_MAJOR_VERSION}"` -1 ))
Severity: Minor
Found in m2install.sh by shellcheck

SC2116 Useless echo? Instead of 'cmd $(echo foo)', just use 'cmd foo'.

Problematic code:

greeting=$(echo "Hello, $name")
# or
tar czf "$(echo "$(date +%F).tar.gz")" *

Correct code:

greeting="Hello, $name"
# or
tar czf "$(date +%F).tar.gz" *

Rationale:

You appear to be using echo to write a value to stdout, and then using $(..) or `..` to capture the value again. This is as pointless as mailing yourself a postcard: you already have what you want, so there's no need to send it on a round trip.

You can just replace $(echo myvalue) with myvalue.

Exceptions

Sometimes this pattern is used because of side effect of echo or expansions. For example, here $(echo ..) is used to expand a glob. glob="*.png" files="$(echo $var)"

The echo is not useless, but this code is problematic because it concatenates filenames by spaces. This will break filenames containing spaces and other characters later when the list is split again. Better options are:

  • Arrays, if supported by the shell: files=( $glob ); echo "The first file is ${files[0]}"
  • Positional parameters when possible: set -- $glob; echo "The first file is $1"
  • Delaying expansion until it's needed: for file in $glob; do ...

All three methods will let you avoid issues with special characters in filenames.

As another example, here $(echo ..) is used to expand escape sequences: unexpanded='var\tvalue' expanded="$(echo "$var")"

In this case, use printf instead. It's well defined with regard to escape sequences.

Finally, if you really do want to concatenate a series of elements by a character like space, consider doing it explicitly with for or printf (e.g. printf '%s\n' $glob).

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Useless echo? Instead of 'cmd $(echo foo)', just use 'cmd foo'.
Open

        B2B_VERSION_MAJOR=$(( `echo "${MAGENTO_MAJOR_VERSION}"` -2 ))
Severity: Minor
Found in m2install.sh by shellcheck

SC2116 Useless echo? Instead of 'cmd $(echo foo)', just use 'cmd foo'.

Problematic code:

greeting=$(echo "Hello, $name")
# or
tar czf "$(echo "$(date +%F).tar.gz")" *

Correct code:

greeting="Hello, $name"
# or
tar czf "$(date +%F).tar.gz" *

Rationale:

You appear to be using echo to write a value to stdout, and then using $(..) or `..` to capture the value again. This is as pointless as mailing yourself a postcard: you already have what you want, so there's no need to send it on a round trip.

You can just replace $(echo myvalue) with myvalue.

Exceptions

Sometimes this pattern is used because of side effect of echo or expansions. For example, here $(echo ..) is used to expand a glob. glob="*.png" files="$(echo $var)"

The echo is not useless, but this code is problematic because it concatenates filenames by spaces. This will break filenames containing spaces and other characters later when the list is split again. Better options are:

  • Arrays, if supported by the shell: files=( $glob ); echo "The first file is ${files[0]}"
  • Positional parameters when possible: set -- $glob; echo "The first file is $1"
  • Delaying expansion until it's needed: for file in $glob; do ...

All three methods will let you avoid issues with special characters in filenames.

As another example, here $(echo ..) is used to expand escape sequences: unexpanded='var\tvalue' expanded="$(echo "$var")"

In this case, use printf instead. It's well defined with regard to escape sequences.

Finally, if you really do want to concatenate a series of elements by a character like space, consider doing it explicitly with for or printf (e.g. printf '%s\n' $glob).

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Use $(..) instead of legacy ...
Open

  [ ! -L "`pwd`/${websiteDir}/pub" ] && ln -s `pwd`/pub "`pwd`/${websiteDir}/pub"
Severity: Minor
Found in m2install.sh by shellcheck

Use $(STATEMENT) instead of legacy `STATEMENT`

Problematic code

echo "Current time: `date`"

Correct code

echo "Current time: $(date)"

Rationale

Backtick command substitution `STATEMENT` is legacy syntax with several issues.

  1. It has a series of undefined behaviors related to quoting in POSIX.
  2. It imposes a custom escaping mode with surprising results.
  3. It's exceptionally hard to nest.

$(STATEMENT) command substitution has none of these problems, and is therefore strongly encouraged.

Exceptions

None.

See also

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Use $(..) instead of legacy ...
Open

  local yellow=`tput setaf 3`
Severity: Minor
Found in m2install.sh by shellcheck

Use $(STATEMENT) instead of legacy `STATEMENT`

Problematic code

echo "Current time: `date`"

Correct code

echo "Current time: $(date)"

Rationale

Backtick command substitution `STATEMENT` is legacy syntax with several issues.

  1. It has a series of undefined behaviors related to quoting in POSIX.
  2. It imposes a custom escaping mode with surprising results.
  3. It's exceptionally hard to nest.

$(STATEMENT) command substitution has none of these problems, and is therefore strongly encouraged.

Exceptions

None.

See also

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Use $(..) instead of legacy ...
Open

  [ ! -L "`pwd`/${websiteDir}/pub" ] && ln -s `pwd`/pub "`pwd`/${websiteDir}/pub"
Severity: Minor
Found in m2install.sh by shellcheck

Use $(STATEMENT) instead of legacy `STATEMENT`

Problematic code

echo "Current time: `date`"

Correct code

echo "Current time: $(date)"

Rationale

Backtick command substitution `STATEMENT` is legacy syntax with several issues.

  1. It has a series of undefined behaviors related to quoting in POSIX.
  2. It imposes a custom escaping mode with surprising results.
  3. It's exceptionally hard to nest.

$(STATEMENT) command substitution has none of these problems, and is therefore strongly encouraged.

Exceptions

None.

See also

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Use $(..) instead of legacy ...
Open

    MAGENTO_MAJOR_VERSION=`echo "${REAL_MAGENTO_VERSION}" | sed 's/.*2\.\([0-9]*\)\.\([0-9]*\).*/\1/'`
Severity: Minor
Found in m2install.sh by shellcheck

Use $(STATEMENT) instead of legacy `STATEMENT`

Problematic code

echo "Current time: `date`"

Correct code

echo "Current time: $(date)"

Rationale

Backtick command substitution `STATEMENT` is legacy syntax with several issues.

  1. It has a series of undefined behaviors related to quoting in POSIX.
  2. It imposes a custom escaping mode with surprising results.
  3. It's exceptionally hard to nest.

$(STATEMENT) command substitution has none of these problems, and is therefore strongly encouraged.

Exceptions

None.

See also

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Use $(..) instead of legacy ...
Open

    REAL_MAGENTO_VERSION=`${BIN_PHP} bin/magento --version`
Severity: Minor
Found in m2install.sh by shellcheck

Use $(STATEMENT) instead of legacy `STATEMENT`

Problematic code

echo "Current time: `date`"

Correct code

echo "Current time: $(date)"

Rationale

Backtick command substitution `STATEMENT` is legacy syntax with several issues.

  1. It has a series of undefined behaviors related to quoting in POSIX.
  2. It imposes a custom escaping mode with surprising results.
  3. It's exceptionally hard to nest.

$(STATEMENT) command substitution has none of these problems, and is therefore strongly encouraged.

Exceptions

None.

See also

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Use $(..) instead of legacy ...
Open

        B2B_VERSION_MAJOR=$(( `echo "${MAGENTO_MAJOR_VERSION}"` -2 ))
Severity: Minor
Found in m2install.sh by shellcheck

Use $(STATEMENT) instead of legacy `STATEMENT`

Problematic code

echo "Current time: `date`"

Correct code

echo "Current time: $(date)"

Rationale

Backtick command substitution `STATEMENT` is legacy syntax with several issues.

  1. It has a series of undefined behaviors related to quoting in POSIX.
  2. It imposes a custom escaping mode with surprising results.
  3. It's exceptionally hard to nest.

$(STATEMENT) command substitution has none of these problems, and is therefore strongly encouraged.

Exceptions

None.

See also

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

See if you can use ${variable//search/replace} instead.
Open

    MAGENTO_PATCH_VERSION=`echo "${REAL_MAGENTO_VERSION}" | sed 's/.*2\.\([0-9]*\)\.\([0-9]*\).\([a-z0-9]*\)/\3/'`
Severity: Minor
Found in m2install.sh by shellcheck

SC2001: See if you can use ${variable//search/replace} instead.

Problematic code:

string="stirng" ; echo "$string" | sed -e "s/ir/ri/"

Correct code:

string="stirng" ; echo "${string//ir/ri}"

Rationale:

Let's assume somewhere earlier in your code you have put data into a variable (Ex: $string). Now you want to do a search and replace inside the contents of $string and echo the contents out. You could pass this to sed as done in the example above, but for simple substitutions utilizing the shell for the same feature is a lot simpler and should be utilized whenever possible.

Exceptions

Occasionally a more complex sed substitution is required. For example, getting the last character of a string.

string="stirng" ; echo "$string" | sed -e "s/^.*\(.\)$/\1/"

This is a bit simple for the example and there are alternative ways of doing this in the shell, but this SC2001 flags on several of my crazy complex sed commands which are beyond the scope of this example. Utilizing some of the more complex capabilities of sed is required occasionally and it is safe to ignore SC2001.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

See if you can use ${variable//search/replace} instead.
Open

    MAGENTO_MAJOR_VERSION=`echo "${REAL_MAGENTO_VERSION}" | sed 's/.*2\.\([0-9]*\)\.\([0-9]*\).*/\1/'`
Severity: Minor
Found in m2install.sh by shellcheck

SC2001: See if you can use ${variable//search/replace} instead.

Problematic code:

string="stirng" ; echo "$string" | sed -e "s/ir/ri/"

Correct code:

string="stirng" ; echo "${string//ir/ri}"

Rationale:

Let's assume somewhere earlier in your code you have put data into a variable (Ex: $string). Now you want to do a search and replace inside the contents of $string and echo the contents out. You could pass this to sed as done in the example above, but for simple substitutions utilizing the shell for the same feature is a lot simpler and should be utilized whenever possible.

Exceptions

Occasionally a more complex sed substitution is required. For example, getting the last character of a string.

string="stirng" ; echo "$string" | sed -e "s/^.*\(.\)$/\1/"

This is a bit simple for the example and there are alternative ways of doing this in the shell, but this SC2001 flags on several of my crazy complex sed commands which are beyond the scope of this example. Utilizing some of the more complex capabilities of sed is required occasionally and it is safe to ignore SC2001.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

See if you can use ${variable//search/replace} instead.
Open

    MAGENTO_MINOR_VERSION=`echo "${REAL_MAGENTO_VERSION}" | sed 's/.*2\.\([0-9]*\)\.\([0-9]*\).*/\2/'`
Severity: Minor
Found in m2install.sh by shellcheck

SC2001: See if you can use ${variable//search/replace} instead.

Problematic code:

string="stirng" ; echo "$string" | sed -e "s/ir/ri/"

Correct code:

string="stirng" ; echo "${string//ir/ri}"

Rationale:

Let's assume somewhere earlier in your code you have put data into a variable (Ex: $string). Now you want to do a search and replace inside the contents of $string and echo the contents out. You could pass this to sed as done in the example above, but for simple substitutions utilizing the shell for the same feature is a lot simpler and should be utilized whenever possible.

Exceptions

Occasionally a more complex sed substitution is required. For example, getting the last character of a string.

string="stirng" ; echo "$string" | sed -e "s/^.*\(.\)$/\1/"

This is a bit simple for the example and there are alternative ways of doing this in the shell, but this SC2001 flags on several of my crazy complex sed commands which are beyond the scope of this example. Utilizing some of the more complex capabilities of sed is required occasionally and it is safe to ignore SC2001.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Use $(..) instead of legacy ...
Open

  local green=`tput setaf 2`
Severity: Minor
Found in m2install.sh by shellcheck

Use $(STATEMENT) instead of legacy `STATEMENT`

Problematic code

echo "Current time: `date`"

Correct code

echo "Current time: $(date)"

Rationale

Backtick command substitution `STATEMENT` is legacy syntax with several issues.

  1. It has a series of undefined behaviors related to quoting in POSIX.
  2. It imposes a custom escaping mode with surprising results.
  3. It's exceptionally hard to nest.

$(STATEMENT) command substitution has none of these problems, and is therefore strongly encouraged.

Exceptions

None.

See also

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Use $(..) instead of legacy ...
Open

        B2B_VERSION_MAJOR=$(( `echo "${MAGENTO_MAJOR_VERSION}"` -1 ))
Severity: Minor
Found in m2install.sh by shellcheck

Use $(STATEMENT) instead of legacy `STATEMENT`

Problematic code

echo "Current time: `date`"

Correct code

echo "Current time: $(date)"

Rationale

Backtick command substitution `STATEMENT` is legacy syntax with several issues.

  1. It has a series of undefined behaviors related to quoting in POSIX.
  2. It imposes a custom escaping mode with surprising results.
  3. It's exceptionally hard to nest.

$(STATEMENT) command substitution has none of these problems, and is therefore strongly encouraged.

Exceptions

None.

See also

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Use $(..) instead of legacy ...
Open

  local red=`tput setaf 1`
Severity: Minor
Found in m2install.sh by shellcheck

Use $(STATEMENT) instead of legacy `STATEMENT`

Problematic code

echo "Current time: `date`"

Correct code

echo "Current time: $(date)"

Rationale

Backtick command substitution `STATEMENT` is legacy syntax with several issues.

  1. It has a series of undefined behaviors related to quoting in POSIX.
  2. It imposes a custom escaping mode with surprising results.
  3. It's exceptionally hard to nest.

$(STATEMENT) command substitution has none of these problems, and is therefore strongly encouraged.

Exceptions

None.

See also

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Use $(..) instead of legacy ...
Open

    MAGENTO_PATCH_VERSION=`echo "${REAL_MAGENTO_VERSION}" | sed 's/.*2\.\([0-9]*\)\.\([0-9]*\).\([a-z0-9]*\)/\3/'`
Severity: Minor
Found in m2install.sh by shellcheck

Use $(STATEMENT) instead of legacy `STATEMENT`

Problematic code

echo "Current time: `date`"

Correct code

echo "Current time: $(date)"

Rationale

Backtick command substitution `STATEMENT` is legacy syntax with several issues.

  1. It has a series of undefined behaviors related to quoting in POSIX.
  2. It imposes a custom escaping mode with surprising results.
  3. It's exceptionally hard to nest.

$(STATEMENT) command substitution has none of these problems, and is therefore strongly encouraged.

Exceptions

None.

See also

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Check exit code directly with e.g. 'if mycmd;', not indirectly with $?.
Open

        if [ 0 = $? ]
Severity: Minor
Found in m2install.sh by shellcheck

Check exit code directly with e.g. 'if mycmd;', not indirectly with $?.

Problematic code:

make mytarget

if [ $? -ne 0 ]
then
  echo "Build failed"
fi

Correct code:

if ! make mytarget
then
  echo "Build failed"
fi

Rationale:

Running a command and then checking its exit status $? against 0 is redundant.

Instead of just checking the exit code of a command, it checks the exit code of a command (e.g. [) that checks the exit code of a command.

Apart from the redundancy, there are other reasons to avoid this pattern:

  • Since the command and its status test are decoupled, inserting an innocent command like echo "make finished" after make will cause the if statement to silently start comparing echo's status instead.
  • Scripts that run or are called with set -e aka errexit will exit immediately if the command fails, even though they're followed by a clause that handles failure.
  • The value of $? is overwritten by [/[[, so you can't get the original value in the relevant then/else block (e.g. if mycmd; then echo "Success"; else echo "Failed with $?"; fi).

To check that a command returns success, use if mycommand; then ....

To check that a command returns failure, use if ! mycommand; then ....

To additionally capture output with command substitution: if output=$(mycommand); then ...

This also applies to while/until loops.

Exceptions:

The default Solaris 10 bourne shell does not support '!' outside of the test command (if ! mycommand; then ... returns !: not found)

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

Useless echo? Instead of 'echo $(cmd)', just use 'cmd'.
Open

  [[ -f bin/magento ]] && { echo "$(parseMagentoVersion)"; return 0; }
Severity: Minor
Found in m2install.sh by shellcheck

Useless echo? Instead of echo $(cmd), just use cmd

Problematic code:

echo "$(cat 1.txt)"
echo `< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c6`

Correct code:

cat 1.txt # In bash, but faster and still sticks exactly one newline: printf '%s\n' "$(<1.txt)"
# The original `echo` sticks a newline; we want it too.
< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c6; echo

Rationale

The command substitution $(foo) yields the result of command foo with trailing newlines erased, and when it is passed to echo it generally just gives the same result as foo.

Exceptions

One may want to use command substitutions plus echo to make sure there is exactly one trailing newline. The special command substitution $(<file) in bash is also un-outline-able.

Anyway, echo is still not that reliable (see [[SC2039#echo-flags]]) and printf should be used instead.

Notice

Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.

There are no issues that match your filters.

Category
Status