Showing 1,039 of 1,039 total issues
Consider using { cmd1; cmd2; } >> file instead of individual redirects. Open
echo '<html lang="en">' >> index.html
- Read upRead up
- Exclude checks
Consider using { cmd1; cmd2; } >> file instead of individual redirects.
Problematic code:
echo foo >> file
date >> file
cat stuff >> file
Correct code:
{
echo foo
date
cat stuff
} >> file
Rationale:
Rather than adding >> something
after every single line, you can simply group the relevant commands and redirect the group.
Exceptions
This is mainly a stylistic issue, and can freely be ignored.
Notice
Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.
You need a space after the '{'. Open
{{% if 'ubuntu' in product %}}
- Read upRead up
- Exclude checks
You need a space after the '{'.
Problematic code:
foo() {echo "hello world;}
Correct code:
foo() { echo "hello world;}
Rationale:
{
is only recognized as the start of a command group when it's a separate token.
If it's not a separate token, like in the problematic example, it will be considered a literal character, as if writing "{echo"
with quotes, and therefore usually cause a syntax error.
Exceptions:
None.
Notice
Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.
This } is literal. Check expression (missing ;/\n?) or quote it. Open
{{% if 'ubuntu' in product %}}
- Read upRead up
- Exclude checks
This {
/}
is literal. Check expression (missing ;/\n?
) or quote it.
Problematic code:
rmf() { rm -f "$@" }
or
eval echo \${foo}
Correct code:
rmf() { rm -f "$@"; }
and
eval "echo \${foo}"
Rationale:
Curly brackets are normally used as syntax in parameter expansion, command grouping and brace expansion.
However, if they don't appear alone at the start of an expression or as part of a parameter or brace expansion, the shell silently treats them as literals. This frequently indicates a bug, so ShellCheck warns about it.
In the example function, the }
is literal because it's not at the start of an expression. We fix it by adding a ;
before it.
In the example eval, the code works fine. However, we can quiet the warning and follow good practice by adding quotes around the literal data.
ShellCheck does not warn about {}
, since this is frequently used with find
and rarely indicates a bug.
Exceptions
This error is harmless when the curly brackets are supposed to be literal, in e.g. awk {'print $1'}
. However, it's cleaner and less error prone to simply include them inside the quotes: awk '{print $1}'
.
Notice
Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.
You need a space after the '{'. Open
{{% endif %}}
- Read upRead up
- Exclude checks
You need a space after the '{'.
Problematic code:
foo() {echo "hello world;}
Correct code:
foo() { echo "hello world;}
Rationale:
{
is only recognized as the start of a command group when it's a separate token.
If it's not a separate token, like in the problematic example, it will be considered a literal character, as if writing "{echo"
with quotes, and therefore usually cause a syntax error.
Exceptions:
None.
Notice
Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.
Missing '}'. Fix any mentioned problems and try again. Open
- Read upRead up
- Exclude checks
Unexpected ..
Note: There is a [known bug](../issues/1036) in the current version when [directives](../wiki/Directive) appear within then
clauses of if
blocks that causes Shellcheck to report SC1072 on otherwise valid code. Avoid using directives within then
clauses - instead place them at the top of the if
block or another enclosing block. This is fixed on the online version and the next release.
See Parser Error.
Notice
Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.
Consider using { cmd1; cmd2; } >> file instead of individual redirects. Open
echo "<h4>Product: ${product}</h4>" >> index.html
- Read upRead up
- Exclude checks
Consider using { cmd1; cmd2; } >> file instead of individual redirects.
Problematic code:
echo foo >> file
date >> file
cat stuff >> file
Correct code:
{
echo foo
date
cat stuff
} >> file
Rationale:
Rather than adding >> something
after every single line, you can simply group the relevant commands and redirect the group.
Exceptions
This is mainly a stylistic issue, and can freely be ignored.
Notice
Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.
Double quote to prevent globbing and word splitting. Open
echo "<!DOCTYPE html>" > $STATS_DIR/index.html
- Read upRead up
- Exclude checks
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
cp -rf build/$product/profile-statistics $STATS_DIR/$product/profile-statistics
- Read upRead up
- Exclude checks
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 popd "$@" if function's $1 should mean script's $1. Open
popd
- Read upRead up
- Exclude checks
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.
Double quote to prevent globbing and word splitting. Open
DS="/tmp/$(basename $DS)"
- Read upRead up
- Exclude checks
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.
You need a space after the '{'. Open
{{% endif %}}
- Read upRead up
- Exclude checks
You need a space after the '{'.
Problematic code:
foo() {echo "hello world;}
Correct code:
foo() { echo "hello world;}
Rationale:
{
is only recognized as the start of a command group when it's a separate token.
If it's not a separate token, like in the problematic example, it will be considered a literal character, as if writing "{echo"
with quotes, and therefore usually cause a syntax error.
Exceptions:
None.
Notice
Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.
This } is literal. Check expression (missing ;/\n?) or quote it. Open
{{% endif %}}
- Read upRead up
- Exclude checks
This {
/}
is literal. Check expression (missing ;/\n?
) or quote it.
Problematic code:
rmf() { rm -f "$@" }
or
eval echo \${foo}
Correct code:
rmf() { rm -f "$@"; }
and
eval "echo \${foo}"
Rationale:
Curly brackets are normally used as syntax in parameter expansion, command grouping and brace expansion.
However, if they don't appear alone at the start of an expression or as part of a parameter or brace expansion, the shell silently treats them as literals. This frequently indicates a bug, so ShellCheck warns about it.
In the example function, the }
is literal because it's not at the start of an expression. We fix it by adding a ;
before it.
In the example eval, the code works fine. However, we can quiet the warning and follow good practice by adding quotes around the literal data.
ShellCheck does not warn about {}
, since this is frequently used with find
and rarely indicates a bug.
Exceptions
This error is harmless when the curly brackets are supposed to be literal, in e.g. awk {'print $1'}
. However, it's cleaner and less error prone to simply include them inside the quotes: awk '{print $1}'
.
Notice
Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.
This } is literal. Check expression (missing ;/\n?) or quote it. Open
{{% endif %}}
- Read upRead up
- Exclude checks
This {
/}
is literal. Check expression (missing ;/\n?
) or quote it.
Problematic code:
rmf() { rm -f "$@" }
or
eval echo \${foo}
Correct code:
rmf() { rm -f "$@"; }
and
eval "echo \${foo}"
Rationale:
Curly brackets are normally used as syntax in parameter expansion, command grouping and brace expansion.
However, if they don't appear alone at the start of an expression or as part of a parameter or brace expansion, the shell silently treats them as literals. This frequently indicates a bug, so ShellCheck warns about it.
In the example function, the }
is literal because it's not at the start of an expression. We fix it by adding a ;
before it.
In the example eval, the code works fine. However, we can quiet the warning and follow good practice by adding quotes around the literal data.
ShellCheck does not warn about {}
, since this is frequently used with find
and rarely indicates a bug.
Exceptions
This error is harmless when the curly brackets are supposed to be literal, in e.g. awk {'print $1'}
. However, it's cleaner and less error prone to simply include them inside the quotes: awk '{print $1}'
.
Notice
Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.
Double quote to prevent globbing and word splitting. Open
echo "<h1>Statistics</h1>" >> $STATS_DIR/index.html
- Read upRead up
- Exclude checks
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
. $SHARED/utils.sh
- Read upRead up
- Exclude checks
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
umount -l ${path}
- Read upRead up
- Exclude checks
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 find instead of ls to better handle non-alphanumeric filenames. Open
log_number=$(ls *.ninja_log | cut -d'.' -f1 | sort -rn | head -n 1)
- Read upRead up
- Exclude checks
Use find instead of ls to better handle non-alphanumeric filenames.
Problematic code:
ls -l | grep " $USER " | grep '\.txt$'
Correct code:
find . -maxdepth 1 -name '*.txt' -user "$USER"
Rationale:
ls
is only intended for human consumption: it has a loose, non-standard format and may "clean up" filenames to make output easier to read.
Here's an example:
$ ls -l
total 0
-rw-r----- 1 me me 0 Feb 5 20:11 foo?bar
-rw-r----- 1 me me 0 Feb 5 2011 foo?bar
-rw-r----- 1 me me 0 Feb 5 20:11 foo?bar
It shows three seemingly identical filenames, and did you spot the time format change? How it formats and what it redacts can differ between locale settings, ls
version, and whether output is a tty.
ls
can usually be substituted for find
if it's the filenames you're after.
If trying to parse out any other fields, first see whether stat
(GNU, OS X, FreeBSD) or find -printf
(GNU) can give you the data you want directly.
Exceptions:
If the information is intended for the user and not for processing (ls -l ~/dir | nl; echo "Ok to delete these files?"
) you can ignore this error with a [[directive]].
Notice
Original content from the ShellCheck https://github.com/koalaman/shellcheck/wiki.
Double quote to prevent globbing and word splitting. Open
cp $DS /tmp || exit 1
- Read upRead up
- Exclude checks
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
sed -i "/<.*Rule.*id=\"$rule/s/selected=\"true\"/selected=\"false\"/g" $DS || exit 1
- Read upRead up
- Exclude checks
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
PACKAGES="$@"
- Read upRead up
- Exclude checks
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.