Skip to content
Open
Changes from 3 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
59 changes: 59 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,62 @@ 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 =
"In pipefail mode, grep -q may cause the pipeline to fail. Use a non-pipe input like '< <(cmd)', '<<<' or 'grep pattern > /dev/null' instead."
Comment thread
mclasmeier marked this conversation as resolved.
Outdated


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