Use semicolon or linefeed before done
(or quote to make it literal).
(or do
then
, fi
, esac
)
Problematic code:
for f in *; do echo "$f" done
or
echo $f is done
Correct code:
for f in *; do echo "$f"; done
or
echo "$f is done"
Rationale:
ShellCheck found a keyword like done
, then
, fi
, esac
, etc used as the argument of a command. This means that the shell will interpret it as a literal string rather than a shell keyword. To be interpreted as a keyword, it must be the first word in the line (i.e. after ;
, &
or a linefeed).
In the example, echo "$f" done
is the same as echo "$f" "done"
, and the done
does not terminate the loop. This is fixed by terminating the echo
command with a ;
so that the done
is the first word in the next line.
Exceptions
If you're intentionally using done
as a literal, you can quote it to make this clear to ShellCheck (and also human readers), e.g. instead of echo Task is done
, use echo "Task is done"
. This makes no difference to the shell, but it will silence this warning.
From POSIX-2018, section "C.2.10 Shell Grammar," regarding the syntax, if (false) then (echo x) else (echo y) fi
:
https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html#tag_23_02_10