What does SC2063 mean?
SC2063, "Grep uses regex, but this looks like a glob", is ShellCheck's warning-level finding that a grep pattern has the shape of a shell wildcard, most often a leading *, when grep interprets every pattern as a regular expression. The two languages share characters and mean different things by them. * in a glob is "anything"; * in a regex is "repeat the previous item", and at the start of a pattern there is no previous item.
What actually breaks?
A manifest of what a log rotation produced, and a script that decides whether there is anything to prune. Run on this box with GNU grep 3.12.
Two archives in the file, "nothing to prune", exit 0. Nothing failed; grep did exactly what the pattern said. What it said:
A leading * in a basic regular expression is a literal asterisk, so the pattern matches an asterisk followed by any character and gz. The only line that can match is one with a real * in it. A prune script that keys off this test never prunes, and the disk fills at the pace of the rotation.
What does ShellCheck say, and what is the fix?
Write the regular expression that says "ends in .gz". The dot has to be escaped, because an unescaped . matches any character, and the $ anchors it to the end of the line:
When the text is meant literally, asterisk included, -F switches regex interpretation off: grep -F '*.gz' matches only a literal *.gz and is clean under ShellCheck. And when the thing being matched is a set of filenames in a directory, grep is the wrong tool altogether. for f in *.gz or compgen -G '*.gz' > /dev/null asks the shell, which does speak glob.
Where else does SC2063 show up?
Crontab checks. crontab -l | grep '*/5' is the same bug: the pattern starts with *. Escape it, grep '\*/5', or fix the string, grep -F '*/5'. Both pass.
Patterns that are legal but wrong. grep 'app*.log' does not trigger SC2063 because it is a valid regex. ShellCheck emits SC2022 instead, a note that p* matches ppp but not papa. Run for real it matched all three app.log* lines in the manifest above, which happens to be what the author wanted, and it would also match apXlog. grep 'app.*\.log' says what was meant.
-E on GNU grep. grep -E '*.gz' still earns SC2063. GNU grep 3.12 prints warning: * at start of expression, then matches the .gz lines anyway by treating the stray * as nothing. That is GNU-specific behaviour behind a warning, not a fix.
ls | grep. ls | grep '*.gz' picks up SC2010 as well, because parsing ls breaks on any unusual filename. The glob is the answer there too.
Patterns ShellCheck 0.11.0 passes without comment: '.*gz', '\.gz$', -F '*.gz', '\*.gz', -E 'app.*\.log'.
When should I disable SC2063, and how?
Almost never. The check fires when a pattern cannot do what it looks like it does, and every legitimate reading has a spelling that passes: \* for a literal asterisk, -F for a fixed string, a real regex for a pattern. If a directive is still wanted, it goes on the line above:
For a single run, shellcheck --exclude=SC2063 script.sh; for a project, disable=SC2063 in .shellcheckrc; to lint for this rule alone, shellcheck --include=SC2063 script.sh.
Related codes
- SC2022 — a regex
*that does not mean what a glob*means. - SC2062 — a grep pattern left unquoted, so the shell expands it before
grepruns. - SC2010 — parsing
lsoutput withgrep. - SC2061 — the same confusion in
find -name.
The grep Pattern Builder shows the regex and fixed-string forms side by side. Any other code: paste it into the ShellCheck Error Decoder.