Skip to content
Open
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions src/ShellCheck/Checks/Commands.hs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ commandChecks = [
,checkXargsDashi
,checkUnquotedEchoSpaces
,checkEvalArray
,checkGrepQPipefail
,checkEgrepQPipefail
,checkFgrepQPipefail
]
++ map checkArgComparison ("alias" : declaringCommands)
++ map checkMaskedReturns declaringCommands
Expand Down Expand Up @@ -400,6 +403,65 @@ checkGrepRe = CommandCheck (Basename "grep") check where
contra = mkRegex "[^a-zA-Z1-9]\\*|[][^$+\\\\]"


prop_checkGrepQPipefail1 = verify checkGrepQPipefail "set -o pipefail; cat file | grep -q pattern"
prop_checkGrepQPipefail2 = verify checkGrepQPipefail "set -o pipefail; cat file | grep --quiet pattern"
prop_checkGrepQPipefail3 = verify checkGrepQPipefail "set -o pipefail; cat file | grep -iq pattern"
prop_checkGrepQPipefail4 = verify checkGrepQPipefail "set -o pipefail; cmd1 | cmd2 | grep -q pattern"
prop_checkGrepQPipefail5 = verify checkGrepQPipefail "set -euo pipefail; cmd | grep -q foo"
prop_checkGrepQPipefail6 = verify checkGrepQPipefail "set -o pipefail; cmd | grep -m 2 foo | cmd2"
prop_checkGrepQPipefail7 = verify checkGrepQPipefail "set -o pipefail; cmd | grep -L foo | cmd2"

prop_checkGrepQPipefailN1 = verifyNot checkGrepQPipefail "cat file | grep -q pattern"
prop_checkGrepQPipefailN2 = verifyNot checkGrepQPipefail "set -o pipefail; grep -q pattern file"
prop_checkGrepQPipefailN3 = verifyNot checkGrepQPipefail "set -o pipefail; cat file | grep pattern"
prop_checkGrepQPipefailN4 = verifyNot checkGrepQPipefail "set -o pipefail; grep -q pattern | cat"
prop_checkGrepQPipefailN5 = verifyNot checkGrepQPipefail "grep -q pattern file"
prop_checkGrepQPipefailN6 = verifyNot checkGrepQPipefail "set -o pipefail; cmd1 | bash -c 'grep -q pattern file'"
prop_checkGrepQPipefailN7 = verifyNot checkGrepQPipefail "set -o pipefail; cmd1 | grep -e -q"
prop_checkGrepQPipefailN8 = verifyNot checkGrepQPipefail "set -o pipefail; cmd1 | grep -eq pattern"
prop_checkGrepQPipefailN9 = verifyNot checkGrepQPipefail "set -o pipefail; cmd1 | grep --regexp -q"
prop_checkGrepQPipefailN10 = verifyNot checkGrepQPipefail "set -o pipefail; cmd1 | grep -- -q"

checkGrepQPipefail = CommandCheck (Basename "grep") checkQuietGrepInPipefailImpl

prop_checkEgrepQPipefail1 = verify checkEgrepQPipefail "set -o pipefail; cat file | egrep -q pattern"
checkEgrepQPipefail = CommandCheck (Basename "egrep") checkQuietGrepInPipefailImpl

prop_checkFgrepQPipefail1 = verify checkFgrepQPipefail "set -o pipefail; cat file | fgrep -q pattern"
checkFgrepQPipefail = CommandCheck (Basename "fgrep") checkQuietGrepInPipefailImpl

-- Catches occurrences of "grep -q" and variants inside of pipes under pipefail.
checkQuietGrepInPipefailImpl cmd = do
pipefail <- asks hasPipefail
Comment thread
mclasmeier marked this conversation as resolved.
Outdated
astPath <- getPathM cmd
sequence_ $ do
guard pipefail
opts <- map fst <$> parseGrepOpts (arguments cmd)
guard $ any isEarlyExitFlag opts
_simpleCmd:grepRedirectingCmd:parentNodes <- Just $ NE.toList astPath
T_Pipeline _ _ (_first:redirectingCmds) <- listToMaybe parentNodes
guard $ any (\node -> getId node == getId grepRedirectingCmd) redirectingCmds
return $ warn (getId cmd) 2337 warnMsg
where
-- Contains "L", even though BSD grep does not exit early with this flag,
-- but GNU grep does. This is consistent with the linter practice of warning
-- about potential problems, while also allowing users to disable specific
-- linter checks locally.
earlyExitFlags = ["q", "quiet", "m", "max-count", "L"]
isEarlyExitFlag name = name `elem` earlyExitFlags
parseGrepOpts = getOpts (True, True)
"cilLnoqsvwxhHrRbaEFGPe:f:m:A:B:C:d:D:"
(map (\name -> (name, True)) longOptionsConsumingParameter)
longOptionsConsumingParameter =
["regexp", "file", "max-count", "after-context", "before-context",
"context", "directories", "devices"]
warnMsg = unwords $
[
"In pipefail mode, flags like -q, -m, or -L can cause grep to exit early, aborting the pipeline with SIGPIPE.",
"Use a non-pipe input like '< <(cmd)' or '<<<' instead."
]


prop_checkTrapQuotes1 = verify checkTrapQuotes "trap \"echo $num\" INT"
prop_checkTrapQuotes1a = verify checkTrapQuotes "trap \"echo `ls`\" INT"
prop_checkTrapQuotes2 = verifyNot checkTrapQuotes "trap 'echo $num' INT"
Expand Down
Loading