255 Commits

Author SHA1 Message Date
Vidar Holen
c5479b8ca3 Stable version 0.3.5
This release is dedicated to Maru, internet celebrity cat.
Where would the web be without you? (Runner-up: Tim Berners-Lee)
2014-11-09 16:30:00 -08:00
Vidar Holen
d9dd58bec8 Warn about 'for $var in values'. 2014-11-09 16:22:01 -08:00
Vidar Holen
af1bb93aba Better warnings for repeated ;;s 2014-11-09 14:33:36 -08:00
Vidar Holen
e909c8ac42 More lenient line feed handling in test expressions. 2014-11-08 15:35:06 -08:00
koalaman
93140e31a0 Merge pull request #253 from vlajos/typofixes-vlajos-20141104
typo fixes - https://github.com/vlajos/misspell_fixer
2014-11-04 15:27:56 -08:00
Veres Lajos
97f3834852 typo fixes - https://github.com/vlajos/misspell_fixer 2014-11-04 21:55:42 +00:00
Vidar Holen
0369f43bac Fixed 2148 to not trigger if a shell is specified with -s. 2014-11-01 13:51:19 -07:00
Vidar Holen
eb2eae2888 Don't warn about ${args[@]} when nested in other ${} 2014-11-01 12:44:27 -07:00
Vidar Holen
30c0c1f27d Allow export "foo"="bar" in 2140 2014-11-01 12:20:10 -07:00
Vidar Holen
bff5d11566 Warn about `` in '' 2014-11-01 12:17:12 -07:00
Vidar Holen
eccb9f3f71 Added -or and -print0 to SC2146 2014-11-01 12:07:09 -07:00
Vidar Holen
2814572116 cat "$@" is not UUOC 2014-10-18 19:59:13 -07:00
Vidar Holen
90bafb9aba Fixed bug where (($b)) counted as a positional reference 2014-10-18 19:51:13 -07:00
Vidar Holen
39b88bbaac Removed Arch from readme, added Debian. 2014-10-12 17:13:35 -07:00
Vidar Holen
39805ab200 Don't warn about unpassed parameters in functions using 'set ..'. 2014-10-12 17:10:46 -07:00
Vidar Holen
9dadce96c0 Improve messages for missing 'then' statements. 2014-10-12 16:17:03 -07:00
Vidar Holen
1a0e208cc3 Consider find -exec when warning about vars in single quotes. 2014-10-12 14:00:17 -07:00
Vidar Holen
a69e27b774 Warn about swapped !# in the shebang. 2014-10-11 12:35:45 -07:00
Vidar Holen
b05c12223f Don't trigger SC2004 for (( $$ )) 2014-09-23 10:27:26 -07:00
Vidar Holen
38ead0385b Fixed quoting warnings for variables in $".." 2014-09-23 10:18:28 -07:00
Vidar Holen
9e8a11e57c Merge branch 'master' of github.com:koalaman/shellcheck 2014-09-23 10:12:23 -07:00
Vidar Holen
6b84b35ec0 Don't crash on empty files with -f gcc. 2014-09-23 10:11:15 -07:00
koalaman
669fdf8e5e Merge pull request #226 from aycanirican/patch-1
Update License in ShellCheck.cabal
2014-09-18 07:09:46 -07:00
Aycan iRiCAN
dccfb3c4a1 Update ShellCheck.cabal
Fixed License.
2014-09-18 09:10:07 +03:00
Vidar Holen
40ce949a56 Only warn once per unused variable name. 2014-09-07 12:55:08 -07:00
Vidar Holen
9f3802138f Prevent overlap of 2116 and 2005 in foo $(echo $(bar)) 2014-09-04 08:41:09 -07:00
Vidar Holen
2f3533fff6 Improve warnings for $ in (()). Also improves array subscripts. 2014-08-16 17:08:57 -07:00
Vidar Holen
f9c346cfd7 Ignore SC2033 when passing quoted function names. 2014-08-16 10:45:46 -07:00
Vidar Holen
5f7419ca37 Require a QuickCheck that doesn't break on UTF-8. 2014-08-10 17:16:27 -07:00
Vidar Holen
8494509150 Warn about missing shebangs. 2014-08-09 17:32:42 -07:00
Vidar Holen
8ba1f2fdf2 Better handling of directories and inaccessible files. 2014-08-08 09:36:17 -07:00
Vidar Holen
dbadca9f61 Check PS1/PROMPT_COMMAND/trap for simple variable references 2014-07-27 09:51:48 -07:00
Vidar Holen
0347ce1b7a Warn about quoted ~ in PATH 2014-07-26 13:14:28 -07:00
Vidar Holen
7fbe66e1c6 Warn about ineffectual quotes in a="/foo/'bar baz'"; $a 2014-07-26 12:15:54 -07:00
Vidar Holen
b000b05507 Parse empty and comment-only backtick expansions. 2014-07-26 12:07:59 -07:00
Vidar Holen
39423ddf81 Stable version 0.3.4
This release is dedicated to Kerbal Space Program,
which has delayed the project by at least a month.
2014-07-08 18:43:33 -07:00
Vidar Holen
875c2d2aad Removed Makefile from cabal file 2014-07-08 18:18:32 -07:00
Vidar Holen
64cc7c691a Warn about precedence in find -name -o -name -exec. 2014-06-22 14:16:24 -07:00
Vidar Holen
b9784cbcc0 Parse let arguments as arithmetic expressions. 2014-06-22 13:23:44 -07:00
Vidar Holen
1a3f6aadaf Support indices in array declarations 2014-06-22 10:35:45 -07:00
Vidar Holen
35756c2cd6 Delete outdated Makefile. 2014-06-22 09:16:54 -07:00
Vidar Holen
0fd351404f Allow escape sequences in here documents. 2014-06-16 14:18:29 -07:00
Vidar Holen
4caa7e7900 Removed accidentally submotted debug code 2014-06-10 00:49:43 -07:00
Vidar Holen
c11c0196d5 Fixed broken parsing of <( in arithmetics 2014-06-10 00:42:07 -07:00
Vidar Holen
b035331d4a Fixed failing test 2014-06-10 00:33:54 -07:00
Vidar Holen
d13253973b Updated readme 2014-06-07 23:25:01 -07:00
koalaman
d9c622ae33 Merge pull request #172 from jbnicolai/master
Adds homebrew installation steps to README.
2014-06-07 23:07:38 -07:00
Vidar Holen
aac7d76047 Don't warn when using find -print0 | xargs --null 2014-06-07 22:41:37 -07:00
Vidar Holen
fc421adb45 Reworked arithmetics to allow composite terms 2014-06-07 22:09:34 -07:00
Joshua Appelman
e0d3c6923a Removes trailing whitespace. 2014-06-08 01:42:32 +02:00
Joshua Appelman
9772ba9de4 Adds homebrew installation steps to README. 2014-06-08 01:42:20 +02:00
Vidar Holen
3a944de606 Warn when concatening strings and arrays. 2014-06-07 13:47:40 -07:00
Vidar Holen
3dd592a02a Support ;& and ;;& in case statements 2014-06-07 12:23:42 -07:00
koalaman
61531cbb10 Merge pull request #169 from Dridi/hackage
Add extra source files in the source tarball
2014-06-01 12:46:08 -07:00
Dridi Boukelmoune
d53087f056 Updated Extra-Source-Files accordingly with #165 2014-06-01 21:35:45 +02:00
Dridi Boukelmoune
39756b420e Add extra source files in the source tarball
This way ShellCheck can be entirely built (including the test suite)
from the Hackage tarball.

The source tarball can be generated using:

    cabal sdist
2014-06-01 17:59:24 +02:00
koalaman
52d4efc951 Merge pull request #168 from rodrigosetti/hlint
Collection of HLint fixes
2014-05-31 16:07:51 -07:00
Rodrigo Setti
5dac723593 Collection of HLint fixes
http://community.haskell.org/~ndm/hlint/
2014-05-31 22:20:49 +00:00
koalaman
2364fd58b6 Merge pull request #166 from rodrigosetti/better-gitignore
Using a more complete Haskell .gitignore
2014-05-31 10:42:17 -07:00
Vidar Holen
cde364c97b Updated README with new cabal instructions 2014-05-31 10:40:45 -07:00
Vidar Holen
98b790f87a Removed outdated version comment 2014-05-31 10:16:11 -07:00
Vidar Holen
726a4e5848 Merge branch 'cabal-version' of https://github.com/rodrigosetti/shellcheck into rodrigosetti-cabal-version
Conflicts:
	ShellCheck/Analytics.hs
	ShellCheck/Data.hs
2014-05-31 09:55:07 -07:00
Rodrigo Setti
0a9ed917e7 Test Suite in Cabal (cabal test)
Please run using "cabal test --show-details=streaming", there's a known
issue about this that was fixed in the latest version of cabal:
https://github.com/haskell/cabal/issues/1810
2014-05-31 01:30:23 +00:00
Vidar Holen
b10d31c8b7 Stable version 0.3.3
This release is dedicated to Jarkko Oikarinen, creator of IRC,
the fabric of the author's existence for so many years.
2014-05-29 21:01:34 -07:00
Vidar Holen
133c779701 Also check nested ifs for ssh/ffmpeg in read loops 2014-05-29 20:55:38 -07:00
Rodrigo Setti
b18ee3fdef Using a more complete Haskell .gitignore
Specially for using cabal sandbox.
2014-05-30 00:10:21 +00:00
Rodrigo Setti
3fcc6c44d8 Use version from generated cabal Paths module 2014-05-30 00:08:09 +00:00
Vidar Holen
d830a36bc8 Check for globs in test, e.g. [[ -e file* ]] 2014-05-25 12:04:18 -07:00
Vidar Holen
1af23fd131 Fix SC2051 to only warn about 1..$n and not 1,$n 2014-05-25 11:41:24 -07:00
Vidar Holen
d21b3362b2 Don't warn about splitting in select statements 2014-05-17 12:06:04 -07:00
Vidar Holen
6cd454e88b Suggest grep -q instead of [ "$(.. | grep)" ] 2014-05-17 10:56:36 -07:00
Vidar Holen
0b5f6b9762 Warn about aliases referencing $1/$*/$@ 2014-05-17 09:26:53 -07:00
Vidar Holen
3824e9cfc2 Fixed not recognizing --f=* as option in checkGrepRe 2014-05-15 09:14:57 -07:00
Vidar Holen
fdce0116da Fix parsing {} in regex 2014-05-13 19:20:34 -07:00
Vidar Holen
b069f7ed27 Added a shellcheck-static Makefile target 2014-05-11 13:58:56 -07:00
Vidar Holen
c4181d45d2 Warn about suspicious IFS, such as IFS="\n" 2014-05-10 15:37:02 -07:00
Vidar Holen
680f838c63 Warn about literal, unquoted {/} 2014-05-10 14:07:53 -07:00
Vidar Holen
e6d81ca7b7 Warn about using numerical test operators with strings 2014-05-10 12:37:02 -07:00
Vidar Holen
fd909eeca0 Fix parsing of &;; in case statements 2014-05-10 11:29:30 -07:00
Vidar Holen
deab146fab Don't warn about &&+|| when used with return 2014-05-09 18:08:55 -07:00
Vidar Holen
f9aeabc245 Improved error messages for SC2044/SC2045 2014-05-08 19:38:40 -07:00
Vidar Holen
558d8ffc6c Warn about suspiciously unquoted literal parts like "var="value"" 2014-05-07 21:47:27 -07:00
Vidar Holen
e96c4c3ffa Warn about aliases that expand at define time 2014-05-07 20:14:21 -07:00
Vidar Holen
c566efd442 Warn about UTF-8 BOMs in scripts. 2014-05-03 10:37:12 -07:00
Vidar Holen
47c220d59c Removed noisy SC1000 about unescaped $s 2014-05-03 10:19:01 -07:00
Vidar Holen
4bd902c5c4 Suggested updating cabal-install in the readme 2014-05-03 10:13:36 -07:00
Vidar Holen
033ce6d941 Allow zsh =(..) process substitution 2014-05-02 20:36:38 -07:00
Vidar Holen
6ad3f557fe Don't warn about sed '$s/foo/bar/' 2014-04-19 12:29:49 -07:00
Vidar Holen
d0bad6c057 Suggest grouping redirections when appending on 3+ lines 2014-04-19 11:53:54 -07:00
Vidar Holen
58c362f97c Warn about foo=(bar baz); echo $foo 2014-04-19 10:20:24 -07:00
Vidar Holen
5f568dd207 Merge branch 'master' of github.com:koalaman/shellcheck 2014-04-19 08:52:57 -07:00
Vidar Holen
2c1e414ac5 Only get vars after the last option in read 2014-04-19 08:50:47 -07:00
koalaman
6699109ab8 Merge pull request #139 from MichaelPereira/patch-1
Add pandoc to needed dependencies
2014-04-17 12:14:38 -07:00
Michael Pereira
423ca82296 Add pandoc to needed dependencies
The pandoc command is needed when compiling with make to run the tests
2014-04-17 15:08:37 -04:00
Vidar Holen
0a263579e0 Support for zsh short form for loops and anonymous functions 2014-04-13 13:37:37 -07:00
Vidar Holen
d63406abe4 Prevent SC2101 to collide with 2060 for tr -d [:space:] 2014-04-12 16:43:57 -07:00
Vidar Holen
81956d324d Don't warn when single quoting PROMPT_COMMAND and PS1 2014-04-05 17:08:03 -07:00
Vidar Holen
f549aad809 Suggest grep -c for grep|wc 2014-04-05 16:53:09 -07:00
Vidar Holen
f9f965693d Don't warn about single quoting $0 for xprop 2014-04-05 16:32:59 -07:00
Vidar Holen
727d940e10 Don't warn about expr when using <, > and friends 2014-04-05 16:29:35 -07:00
Vidar Holen
c26c2b8536 Stop using generic char 'c' in 2022 2014-04-05 16:17:52 -07:00
Vidar Holen
d8878ed852 Fixed warnings when assigning arrays to scalars 2014-04-05 14:56:12 -07:00
Vidar Holen
c3cc5f649f Fixed warning about \n in echo -n -e '\n' 2014-04-05 12:49:24 -07:00
koalaman
8bd4365cdb Merge pull request #122 from guywithnose/master
Add cabal update to install instructions
2014-04-05 11:40:46 -07:00
koalaman
a00a6fb53b Merge pull request #125 from inthecloud247/master
Instructions for adding ShellCheck to PATH
2014-04-01 17:59:48 -07:00
John Albietz
3332eba9a0 Instructions for adding ShellCheck to PATH 2014-04-01 14:26:03 -07:00
guywithnose
ad08bb64aa Add cabal update to install instructions
Before I ran `cabal update` I got this on `cabal install`

```
Config file path source is default config file.
Config file ~/.cabal/config not found.
Writing default configuration to ~/.cabal/config
Warning: The package list for 'hackage.haskell.org' does not exist. Run 'cabal
update' to download it.
Resolving dependencies...
cabal: Could not resolve dependencies:
trying: ShellCheck-0.3.2
```
2014-04-01 11:48:09 -04:00
Vidar Holen
f01e6e1a99 Check for accidentally overriding $PATH 2014-03-29 10:07:23 -07:00
Vidar Holen
de0145fb29 Stable version 0.3.2
This release is dedicated to knirch, mcandre, Dridi,
ptman, pihentagy, Riviera, and everyone else who keeps
submitting bug reports and feature suggestions!
2014-03-22 11:05:27 -07:00
Vidar Holen
0d4ae95e1d Recognize declare -x as exporting variables 2014-03-22 10:43:56 -07:00
Vidar Holen
50db49e2fb Rename Unquotable to QuoteFree 2014-03-22 10:27:59 -07:00
Vidar Holen
60aafae21d Count array indexes as references, even without $ 2014-03-22 10:22:34 -07:00
Vidar Holen
902cb9c303 Fixed up README 2014-03-19 09:57:01 -07:00
Vidar Holen
4f1fd43360 Don't suggest removing $ in (( 10#$n )) 2014-03-16 15:06:18 -07:00
Vidar Holen
ca5af5c55a Don't warn about decimals in (( )) for zsh/ksh 2014-03-16 14:56:23 -07:00
Vidar Holen
503cac3bb3 Merge branch 'master' of github.com:koalaman/shellcheck 2014-03-16 14:53:18 -07:00
Vidar Holen
2a9c9ae0ad Warn about using <=/>=, and don't warn about -gt 1.2 in ksh/zsh 2014-03-16 14:51:46 -07:00
koalaman
def4551991 Merge pull request #117 from mcandre/master
readme: markdown for link rendering
2014-03-15 16:17:07 -07:00
Vidar Holen
67f4a0d6eb Accept and warn about capitalization in keywords. 2014-03-15 16:08:33 -07:00
Andrew Pennebaker
f92f934688 readme: added compiler memory note 2014-03-15 16:49:05 -04:00
Andrew Pennebaker
d4059c30b7 readme: markdown for link rendering 2014-03-14 16:37:34 -04:00
Vidar Holen
b68de7f42b Don't warn about for s in "${!var}", it could be an array 2014-03-13 20:54:10 -07:00
Vidar Holen
7dacb62d36 Fixed determining shell for shebangs with flags 2014-03-09 17:24:05 -07:00
Vidar Holen
3423cde931 Check attempts to set variables with 'set' 2014-03-01 16:52:53 -08:00
Vidar Holen
b2d1aa01f7 Don't warn about commas when quoted in a=("a,b") 2014-03-01 15:30:51 -08:00
Vidar Holen
19e1bdf11f Warn about array assignments for /bin/sh 2014-03-01 15:16:31 -08:00
Vidar Holen
75d51087c8 Warn about functions using parameters that are never passed 2014-03-01 14:42:00 -08:00
Vidar Holen
ed524fb77f Don't warn about decimals when comparing with = 2014-03-01 10:24:22 -08:00
Vidar Holen
97045c4af1 Fixed x[0] not recognized as reference of x in arithmetics 2014-03-01 10:14:17 -08:00
Vidar Holen
1b806f6c9f Merge branch 'master' of github.com:koalaman/shellcheck 2014-03-01 10:06:03 -08:00
Vidar Holen
632c1614a1 Added support for |& 2014-03-01 10:05:43 -08:00
koalaman
00d9ef12e7 Merge pull request #105 from Dridi/dynamic_link
Dynamic linking to libShellCheck
2014-03-01 09:00:47 -08:00
Vidar Holen
d07294810b Allow \n before and after ||/&& in [[ ]] 2014-02-28 18:46:10 -08:00
Dridi Boukelmoune
948b750754 Make the executable depend on the library
It needed a bump to 1.8 for the minimum `Cabal-Version'. One downside is
that the executable also build-depends on the same libraries.
Alphabetical order is kept, except for the dependency to the ShellCheck
library itself.
2014-02-28 19:55:04 +01:00
Dridi Boukelmoune
41ae95116d Reformat ShellCheck.cabal for readability
Uses one line per `build-depends' or `exposed-modules'. Also got them
sorted by name. Folded `base' dependencies into a single one.
2014-02-28 19:44:09 +01:00
Vidar Holen
bf3c942294 Warn about using 'su foo' to continue as foo 2014-02-16 18:51:30 -08:00
Vidar Holen
055b40462d Improved $(echo ..) warnings 2014-02-16 13:26:50 -08:00
Vidar Holen
b087b7efb1 Some hlint fixes.
Ironically, this is the first time the linter has been linted.
2014-02-16 12:57:34 -08:00
Vidar Holen
5d8d57cf07 Suggest useless use of echo for $(echo $var) 2014-02-12 19:20:39 -08:00
Vidar Holen
661091a9da Added better message for SC1007, for 'var= value' 2014-02-12 18:26:41 -08:00
Vidar Holen
2ec60c2627 Added double prime to list of unicode quotes. 2014-02-08 14:15:04 -08:00
Vidar Holen
8b4909b238 Improve warnings for missing quotes. 2014-02-08 14:10:45 -08:00
Vidar Holen
95a3be6546 README: Updated URL, reformatted long lines 2014-02-08 09:58:11 -08:00
Vidar Holen
968e34e002 Parse forward ticks (acute accents) just like backticks and warn. 2014-02-08 09:50:20 -08:00
Vidar Holen
197b3e3f20 Some checks for accidental rm -r 2014-02-04 19:43:16 -08:00
Vidar Holen
0e464ea476 Stable version 0.3.1
This release is dedicated to the Flycheck team,
even though ShellCheck is written entirely in Vim.
2014-02-03 20:22:30 -08:00
Vidar Holen
811df6f0da Fixed cabal file 2014-02-03 20:21:26 -08:00
Vidar Holen
4e5d32b05a Added --version flag 2014-02-03 20:06:59 -08:00
Vidar Holen
c5141b77bf Fixed parser not accepting `` in arithmetic contex 2014-02-03 16:45:48 -08:00
Vidar Holen
9dfeb6b42a Added -s to the man page 2014-02-02 21:56:04 -08:00
Vidar Holen
77916d2645 Fixed compilation error on GHC7 2014-02-02 21:47:02 -08:00
Vidar Holen
4968e7d9ff Added -s flag to override dialect, e.g. -s ksh 2014-02-02 19:28:09 -08:00
Vidar Holen
075d58ee90 Replaced parser error for 'function' with shell-aware check. 2014-02-02 13:39:44 -08:00
Vidar Holen
6a4a5a815e Don't consider last stage of pipeline a subshell for Ksh/Zsh
Also fixes the problem where pipelines were considered a single subshell.
2014-02-02 13:03:26 -08:00
Vidar Holen
76a39f254b Refactoring, 25% speedup.
* Checks now use Writer monad instead of State

* Parser no longer emits notes unrelated to parsing.

* All checks are now passed a parameter value, containing shell type,
  map from notes to parents and such. This eliminates recalculation
  and removes the need for a special group of parent examining checks.
2014-02-02 04:59:17 -08:00
Vidar Holen
8ec9fa43fd Warn about break/continue in subshells and outside loops 2014-02-01 23:45:26 -08:00
Vidar Holen
e8634a3c27 Removed duplicate check for [[ a == b + 1 ]] 2014-02-01 20:45:44 -08:00
Vidar Holen
9ae776530b Check for [[ i + 1 = 2 ]] 2014-01-27 22:47:48 -08:00
Vidar Holen
0ec62390d5 Merge branch 'master' of github.com:koalaman/shellcheck 2014-01-27 22:13:04 -08:00
Vidar Holen
82328cd86e Warn about literal "\ " just like literal quotes.
Also, do it recursively.
2014-01-27 22:11:46 -08:00
koalaman
5b58da7249 Merge pull request #75 from michaelsanford/master
Added MacPorts cabal install information to README
2014-01-27 12:11:19 -08:00
michaelsanford
8676517270 Macports install info thanks to @myint
Capital P on MacPorts
2014-01-27 14:41:40 -05:00
Vidar Holen
4262c4b1bf Allow {} in arithmetic for loops 2014-01-26 12:44:51 -08:00
Vidar Holen
7ad0110443 Don't warn about sed '$d' or '$p' 2014-01-25 14:54:05 -08:00
Vidar Holen
e9bba2f75a Don't warn about comma separation in for f in {a,b} 2014-01-25 14:30:25 -08:00
Vidar Holen
74ea5eaeec Parse but warn about "else if" 2014-01-25 14:12:31 -08:00
koalaman
b7ee5f4410 Merge pull request #67 from michaelsanford/master
Added cabal setup instructions for Mac OS X to README
2014-01-24 21:15:10 -08:00
Michael Sanford
e294db171e Added Mac OS instructions with brew (relates #11)
Un-Markdown text
2014-01-24 15:59:10 -05:00
Vidar Holen
8c3d8d7cfa Stable version 0.3.0 2014-01-19 13:04:17 -08:00
Vidar Holen
380d6c3317 Cleaned up cabal file to make 'cabal check' pass. 2014-01-19 13:03:05 -08:00
Vidar Holen
16bd52333a man page: Added Directives section 2014-01-19 12:36:48 -08:00
Vidar Holen
cfb44b3fe2 Warn about 'cd $f; foo; cd ..' loop antipattern. 2014-01-17 09:02:14 -08:00
Vidar Holen
43ed5e748d Allow '# shellcheck disable=SC1234' to ignore by code. 2014-01-16 23:08:56 -08:00
Vidar Holen
4dca88aade Don't warn about quotes in variables for eval 2014-01-16 19:41:09 -08:00
Vidar Holen
1d2c7a8551 Warn about abused char classes, such as [10-15] and [:digit:] 2014-01-14 21:20:21 -08:00
koalaman
ba080e7e34 Merge pull request #61 from cheecheeo/master
expanded cabal file
2014-01-14 10:40:27 -08:00
John Chee
fc716738eb expanded cabal file 2014-01-13 19:14:18 -08:00
Vidar Holen
659709d529 Fixed ${!var} not counting as reference of var 2013-12-30 10:00:05 -08:00
Vidar Holen
5b4729d940 Parser: allow subshell function definitions 2013-12-30 09:50:14 -08:00
Vidar Holen
b936f28763 Merge branch 'manpage' 2013-12-15 19:43:02 -08:00
Vidar Holen
78d9a7ad97 Tweaked man page format 2013-12-15 19:40:05 -08:00
Vidar Holen
d540a98d33 Merge branch 'man_page' of https://github.com/Dridi/shellcheck into manpage 2013-12-15 18:44:49 -08:00
Vidar Holen
8c00850134 Minor performance tweaks (~10% improvement) 2013-12-15 18:43:34 -08:00
Vidar Holen
d1990e3396 Warn about 'i=i+1' and 'i=i + 1' 2013-12-15 16:11:17 -08:00
Dridi Boukelmoune
91fc4a046c Added a shellcheck(1) man page
The manual is mainly a copy/paste from the current documentation. It is
builded with pandoc, and written in markdown.

closes #50
2013-12-16 00:04:31 +01:00
Vidar Holen
95ebe1cd07 Fixed parser error for '..; done| ..' 2013-12-15 14:39:47 -08:00
Vidar Holen
27822a1f56 Warn about 'foo=bar echo $foo' 2013-12-14 16:06:19 -08:00
Vidar Holen
eb06b06475 Don't warn about single quoted vars for ssh 2013-12-14 15:28:58 -08:00
Vidar Holen
5d72432046 If printf string contains %, don't warn about using vars 2013-12-14 15:27:11 -08:00
Vidar Holen
da51b14789 Parser: accept here doc token strings more liberally 2013-12-14 15:20:15 -08:00
Vidar Holen
7be8485b8b Fixed parsing of 'time' in front of compound commands 2013-12-14 14:44:47 -08:00
Vidar Holen
a4d36ba0d2 Warn about while read f; do ssh "$f"; done 2013-11-29 23:05:41 -08:00
Vidar Holen
d4bc0f6e10 Don't require separator before do in 'for i do ..' 2013-11-29 16:22:37 -08:00
Vidar Holen
1011ae7b3c Fixed incorrect parsing of [ foo -a -f bar ] 2013-11-24 18:49:49 -08:00
Vidar Holen
d603ee1e89 Don't warn for A&&B||C if C is echo/exit/assignment 2013-11-24 16:15:10 -08:00
Vidar Holen
4fc518c877 Cleaned up command matching code 2013-11-24 15:48:07 -08:00
Vidar Holen
7fda86d6e2 Fixed warning for 'exec foo; exit 3' 2013-11-24 14:48:12 -08:00
Vidar Holen
6905373b6c Fix incorrect warning for 'foo > bar 2> bar' 2013-11-24 14:20:24 -08:00
Vidar Holen
1d8401d583 Assume variables have spaces by default 2013-11-17 16:16:58 -08:00
Vidar Holen
a89aee1a34 Assume variables have spaces/globs by default 2013-11-17 16:13:45 -08:00
Vidar Holen
4853dce3fe Fixed wrong quoting warning for foo=" "; foo 2013-11-17 15:00:07 -08:00
Vidar Holen
a793e09bab Added hint to suggest missing ;; in case 2013-11-17 14:41:55 -08:00
Vidar Holen
fbd85e93ee Added --exclude to filter out warnings 2013-11-17 13:41:42 -08:00
Vidar Holen
77f754fa32 Replace Prelude.catch with Control.Exception.catch 2013-11-15 17:31:55 -08:00
Vidar Holen
01d557abe6 More discriminating error codes 2013-11-14 22:46:09 -08:00
Vidar Holen
68cc00b6e8 Exit with 1 on comments and 2 on failure 2013-11-14 22:35:38 -08:00
Vidar Holen
8b7c0be06f gcc output mode now uses a tab width of 1 2013-11-14 22:23:55 -08:00
Vidar Holen
473bb666d8 Support for checkstyle compatible xml output 2013-11-13 22:39:35 -08:00
Vidar Holen
376d407ea1 Added gcc compatible output for editor integrations 2013-11-13 17:28:08 -08:00
Vidar Holen
2e13cedc4b Removed jsoncheck. Use 'shellcheck -f json -' instead 2013-11-12 21:39:41 -08:00
Vidar Holen
17515ad706 Added proper command line parsing 2013-11-12 21:22:52 -08:00
Vidar Holen
d8b5d6393a \" should not be treated specially in backticks 2013-11-12 18:27:18 -08:00
Vidar Holen
d404bc703d Show expose error code in shellcheck frontends 2013-11-10 12:22:33 -08:00
Vidar Holen
e5e08df1d9 Numbered messages 2013-11-10 10:55:46 -08:00
koalaman
1988cba147 Merge pull request #33 from daniellawrence/master
updated debian/ubuntu required packages
2013-11-07 10:21:48 -08:00
Daniel Lawrence
4cee7fd27f updated debian/ubuntu required packages 2013-11-04 16:00:47 +11:00
Vidar Holen
b75fe02aac Merge branch 'master' of github.com:koalaman/shellcheck 2013-11-03 13:58:33 -08:00
Vidar Holen
83c3dd3418 Accept ./? in function names 2013-11-03 13:58:06 -08:00
koalaman
020850dbbb Merge pull request #16 from carenas/master
gitignore and makefile for building with cabal and make
2013-11-03 13:51:13 -08:00
Vidar Holen
8d265aa25e Don't warn about expr if using : operator 2013-11-03 13:47:04 -08:00
Vidar Holen
c343217fd2 Added Fedora prerequisites in README 2013-11-03 13:27:10 -08:00
Vidar Holen
71bc26aefa Fixed parsing of | outside of groups in =~ regex 2013-11-03 13:13:24 -08:00
Vidar Holen
8a3d259ae6 Don't warn about single quotes in args to perl 2013-11-03 12:47:44 -08:00
Vidar Holen
3a9ae0ebf1 Accept dir-/basename due to edge cases like "/" and "foo" 2013-11-03 12:42:11 -08:00
Carlo Marcelo Arenas Belon
d6b903e6cc gitignore and makefile for building with cabal and make
makefile should also delete cabal generated files on cleanup so
they could be used interchangably.

ensure that all generated files are ignored as recommended by
bese practices.
2013-10-28 02:56:29 -07:00
Vidar Holen
b9f7f82e29 Stable version 0.2.0 2013-10-27 16:04:33 -07:00
Vidar Holen
6d0bfcf37a Fixed parser accepting spaces after here doc token 2013-10-27 16:02:27 -07:00
Vidar Holen
e0bbb89d00 Fixed parser bug where }> wasn't recognized as Rbrace 2013-10-27 15:36:47 -07:00
Vidar Holen
a0a58d432a Merge branch 'master' of github.com:koalaman/shellcheck 2013-10-20 15:04:45 -07:00
Vidar Holen
206900fb64 Use exit status and stderr properly in terminal tool 2013-10-20 15:03:14 -07:00
koalaman
794a5523d1 Merge pull request #12 from mcandre/master
README: clarify build step
2013-10-20 13:47:15 -07:00
Andrew Pennebaker
389c7b670c README: clarify build step
Help those unfamiliar with cabal to install shellcheck.
2013-10-20 16:33:00 -04:00
Vidar Holen
b1af7bb8f2 Fixed parser error for 'for ((;;)) ; do' 2013-10-11 20:03:55 -07:00
Vidar Holen
157fea73da Suggest ./* instead of * for command arguments 2013-10-06 15:54:31 -07:00
Vidar Holen
b439f02b8e Improved assignment parsing: track indices and += vs = 2013-10-06 14:44:43 -07:00
Vidar Holen
710a28c572 Revert "Fixed parsing for 'eval var=(values)'", because it fails for 'eval $x=foo'
This reverts commit 34e69556b1.
2013-09-29 21:35:20 -07:00
Vidar Holen
702d57b655 Warn about unicode quotes 2013-09-29 21:22:42 -07:00
Vidar Holen
34e69556b1 Fixed parsing for 'eval var=(values)' 2013-09-29 21:00:29 -07:00
Vidar Holen
7c411b39ac Fixed warnings for read -p "Name: " 2013-09-29 20:55:26 -07:00
Vidar Holen
5a959bc340 Fix parser errors for unclosed $( in here docs 2013-09-21 16:27:17 -07:00
Vidar Holen
fb5f72951d Don't warn about unused variables for read '' 2013-09-12 19:11:42 -07:00
Vidar Holen
7630136d6c Move command and variable lists to ShellCheck.Data. 2013-09-12 18:50:33 -07:00
Vidar Holen
dacb8c597f Fixed a series of extglob parsing oddities and edge cases 2013-09-04 13:02:30 -07:00
Vidar Holen
d99aaaf8dc Merge branch 'master' of github.com:koalaman/shellcheck 2013-09-03 14:09:50 -07:00
Vidar Holen
876831b419 Fixed up readme 2013-09-03 14:08:30 -07:00
Vidar Holen
24580609b8 Merge branch 'master' of https://github.com/carenas/shellcheck into carenas-master 2013-09-03 14:06:38 -07:00
koalaman
5828abe324 Merge pull request #7 from carenas/master
Include jsoncheck in 'make clean' target and update required ubuntu package list.
2013-09-03 14:04:49 -07:00
Carlo Marcelo Arenas Belon
c229d3929a build: update recommended Ubuntu package list
last still supported version to use ghc6 was lucid, so it is
better to refer instead to ghc 7 as used since precise
2013-09-01 21:46:32 -07:00
Carlo Marcelo Arenas Belon
31907ca51d build: include all binaries for clean 2013-09-01 21:37:30 -07:00
Vidar Holen
58b8e0ab70 Suggest pgrep when grepping ps 2013-08-31 18:32:07 -07:00
Vidar Holen
9586a46c9c Warn about assigned but unreferenced variables 2013-08-31 18:26:20 -07:00
Vidar Holen
bb49cf8e65 Parse the contents of unquoted here documents 2013-08-31 17:03:15 -07:00
Vidar Holen
de1fa61560 Warn about client side expansion in ssh strings/heredocs. 2013-08-03 21:19:32 -07:00
Vidar Holen
07b1fd6f44 Allow :+- in function names. :(){ :|:;};:, anyone? 2013-08-03 20:22:32 -07:00
Vidar Holen
d0caa1e1df Don't warn for 'find .. | xargs -0' or 'ls -N | ..' 2013-07-25 19:58:53 -07:00
18 changed files with 3713 additions and 1316 deletions

15
.gitignore vendored Normal file
View File

@@ -0,0 +1,15 @@
# Created by http://www.gitignore.io
### Haskell ###
dist
cabal-dev
*.o
*.hi
*.chi
*.chs.h
.virtualenv
.hsenv
.cabal-sandbox/
cabal.sandbox.config
cabal.config

View File

@@ -1,24 +0,0 @@
# TODO: Phase out Makefile in favor of Cabal
GHCFLAGS=-O9
all: shellcheck jsoncheck .tests
: Done
shellcheck: regardless
: Conditionally compiling shellcheck
ghc $(GHCFLAGS) --make shellcheck
jsoncheck: regardless
: Conditionally compiling shellcheck
ghc $(GHCFLAGS) --make jsoncheck
.tests: *.hs */*.hs
: Running unit tests
./test/runQuack && touch .tests
clean:
rm -f .tests shellcheck *.hi *.o ShellCheck/*.hi ShellCheck/*.o
regardless:

26
README
View File

@@ -1,26 +0,0 @@
ShellCheck - A shell script static analysis tool
http://www.vidarholen.net/contents/shellcheck
Copyright 2012, Vidar 'koala_man' Holen
Licensed under the GNU Affero General Public License, v3
The goals of ShellCheck are:
- To point out and clarify typical beginner's syntax issues,
that causes a shell to give cryptic error messages.
- To point out and clarify typical intermediate level semantic problems,
that causes a shell to behave strangely and counter-intuitively.
- To point out subtle caveats, corner cases and pitfalls, that may cause an
advanced user's otherwise working script to fail under future circumstances.
ShellCheck is written in Haskell, and requires GHC, Parsec3 and Text.Regex.
To build the JSON interface and run the unit tests, it also requires QuickCheck2 and JSON.
On Ubuntu and similar, these are called:
ghc6 libghc6-parsec3-dev libghc6-quickcheck2-dev libghc6-json-dev libghc-regex-compat-dev
Executables can be built with cabal. Tests currently still rely on a Makefile.
Happy ShellChecking!

88
README.md Normal file
View File

@@ -0,0 +1,88 @@
# ShellCheck - A shell script static analysis tool
http://www.shellcheck.net
Copyright 2012-2014, Vidar 'koala_man' Holen
Licensed under the GNU Affero General Public License, v3
The goals of ShellCheck are:
- To point out and clarify typical beginner's syntax issues,
that causes a shell to give cryptic error messages.
- To point out and clarify typical intermediate level semantic problems,
that causes a shell to behave strangely and counter-intuitively.
- To point out subtle caveats, corner cases and pitfalls, that may cause an
advanced user's otherwise working script to fail under future circumstances.
ShellCheck is written in Haskell, and requires 2 GB of memory to compile.
## Installing
On systems with Cabal:
cabal update
cabal install shellcheck
On Debian based distros:
apt-get install shellcheck
On OS X with homebrew:
brew install shellcheck
ShellCheck is also available as an online service:
http://www.shellcheck.net
## Building with Cabal
This sections describes how to build ShellCheck from a source directory.
First, make sure cabal is installed. On Debian based distros:
apt-get install cabal-install
On Fedora:
yum install cabal-install
On Mac OS X with homebrew (http://brew.sh/):
brew install cabal-install
On Mac OS X with MacPorts (http://www.macports.org/):
port install hs-cabal-install
Let cabal update itself, in case your distro version is outdated:
$ cabal update
$ cabal install cabal-install
With cabal installed, cd to the ShellCheck source directory and:
$ cabal install
This will install ShellCheck to your ~/.cabal/bin directory.
Add the directory to your PATH (for bash, add this to your ~/.bashrc file):
export PATH=$HOME/.cabal/bin:$PATH
Verify that your PATH is set up correctly:
$ which shellcheck
~/.cabal/bin/shellcheck
## Running tests
To run the unit test suite:
cabal configure --enable-tests
cabal build
cabal test
Happy ShellChecking!

2
Setup.hs Normal file
View File

@@ -0,0 +1,2 @@
import Distribution.Simple
main = defaultMain

View File

@@ -1,20 +1,84 @@
Name: ShellCheck
Version: 0.1.0
Description: Shell script analysis tool
Version: 0.3.5
Synopsis: Shell script analysis tool
License: AGPL-3
License-file: LICENSE
Category: Static Analysis
Author: Vidar Holen
Maintainer: vidar@vidarholen.net
Homepage: http://www.shellcheck.net/
Build-Type: Simple
Cabal-Version: >= 1.2
Cabal-Version: >= 1.8
Bug-reports: https://github.com/koalaman/shellcheck/issues
Description:
The goals of ShellCheck are:
.
* To point out and clarify typical beginner's syntax issues,
that causes a shell to give cryptic error messages.
.
* To point out and clarify typical intermediate level semantic problems,
that causes a shell to behave strangely and counter-intuitively.
.
* To point out subtle caveats, corner cases and pitfalls, that may cause an
advanced user's otherwise working script to fail under future circumstances.
Extra-Source-Files:
-- documentation
README.md
shellcheck.1.md
-- tests
test/shellcheck.hs
source-repository head
type: git
location: git://github.com/koalaman/shellcheck.git
library
build-depends: base >= 4, parsec, containers, regex-compat, mtl, directory
exposed-modules: ShellCheck.AST, ShellCheck.Parser, ShellCheck.Analytics, ShellCheck.Simple
build-depends:
base >= 4 && < 5,
containers,
directory,
json,
mtl,
parsec,
regex-compat,
QuickCheck >= 2.7.4
exposed-modules:
ShellCheck.Analytics
ShellCheck.AST
ShellCheck.Data
ShellCheck.Options
ShellCheck.Parser
ShellCheck.Simple
other-modules:
Paths_ShellCheck
executable shellcheck
build-depends:
ShellCheck,
base >= 4 && < 5,
containers,
directory,
json,
mtl,
parsec,
regex-compat,
transformers,
QuickCheck >= 2.7.4
main-is: shellcheck.hs
executable jsoncheck
build-depends: json
main-is: jsoncheck.hs
test-suite test-shellcheck
type: exitcode-stdio-1.0
build-depends:
ShellCheck,
base >= 4 && < 5,
containers,
directory,
json,
mtl,
parsec,
regex-compat,
transformers,
QuickCheck >= 2.7.4
main-is: test/shellcheck.hs

View File

@@ -23,15 +23,21 @@ import qualified Text.Regex as Re
data Id = Id Int deriving (Show, Eq, Ord)
data Quoted = Quoted | Unquoted deriving (Show, Eq)
data Dashed = Dashed | Undashed deriving (Show, Eq)
data AssignmentMode = Assign | Append deriving (Show, Eq)
data FunctionKeyword = FunctionKeyword Bool deriving (Show, Eq)
data FunctionParentheses = FunctionParentheses Bool deriving (Show, Eq)
data ForInType = NormalForIn | ShortForIn deriving (Show, Eq)
data CaseType = CaseBreak | CaseFallThrough | CaseContinue deriving (Show, Eq)
data Token =
TA_Base Id String Token
| TA_Binary Id String Token Token
| TA_Expansion Id Token
| TA_Literal Id String
TA_Binary Id String Token Token
| TA_Expansion Id [Token]
| TA_Index Id Token
| TA_Sequence Id [Token]
| TA_Trinary Id Token Token Token
| TA_Unary Id String Token
| TA_Variable Id String
| TC_And Id ConditionType String Token Token
| TC_Binary Id ConditionType String Token Token
| TC_Group Id ConditionType Token
@@ -42,7 +48,9 @@ data Token =
| T_AndIf Id (Token) (Token)
| T_Arithmetic Id Token
| T_Array Id [Token]
| T_Assignment Id String Token
| T_IndexedElement Id Token Token
| T_ Id [Token]
| T_Assignment Id AssignmentMode String (Maybe Token) Token
| T_Backgrounded Id Token
| T_Backticked Id [Token]
| T_Bang Id
@@ -51,7 +59,7 @@ data Token =
| T_BraceGroup Id [Token]
| T_CLOBBER Id
| T_Case Id
| T_CaseExpression Id Token [([Token],[Token])]
| T_CaseExpression Id Token [(CaseType, [Token], [Token])]
| T_Condition Id ConditionType Token
| T_DGREAT Id
| T_DLESS Id
@@ -75,12 +83,12 @@ data Token =
| T_Fi Id
| T_For Id
| T_ForArithmetic Id Token Token Token [Token]
| T_ForIn Id String [Token] [Token]
| T_Function Id String Token
| T_ForIn Id ForInType [String] [Token] [Token]
| T_Function Id FunctionKeyword FunctionParentheses String Token
| T_GREATAND Id
| T_Glob Id String
| T_Greater Id
| T_HereDoc Id Bool Bool String
| T_HereDoc Id Dashed Quoted String [Token]
| T_HereString Id Token
| T_If Id
| T_IfExpression Id [([Token],[Token])] [Token]
@@ -96,7 +104,7 @@ data Token =
| T_NormalWord Id [Token]
| T_OR_IF Id
| T_OrIf Id (Token) (Token)
| T_Pipeline Id [Token]
| T_Pipeline Id [Token] [Token] -- [Pipe separators] [Commands]
| T_ProcSub Id String [Token]
| T_Rbrace Id
| T_Redirecting Id [Token] Token
@@ -113,19 +121,22 @@ data Token =
| T_UntilExpression Id [Token] [Token]
| T_While Id
| T_WhileExpression Id [Token] [Token]
| T_Annotation Id [Annotation] Token
| T_Pipe Id String
deriving (Show)
data Annotation = DisableComment Integer deriving (Show, Eq)
data ConditionType = DoubleBracket | SingleBracket deriving (Show, Eq)
-- I apologize for nothing!
lolHax s = Re.subRegex (Re.mkRegex "(Id [0-9]+)") (show s) "(Id 0)"
instance Eq Token where
(==) a b = (lolHax a) == (lolHax b)
(==) a b = lolHax a == lolHax b
analyze :: Monad m => (Token -> m ()) -> (Token -> m ()) -> (Token -> Token) -> Token -> m Token
analyze f g i t =
round t
analyze f g i =
round
where
round t = do
f t
@@ -134,6 +145,11 @@ analyze f g i t =
return . i $ newT
roundAll = mapM round
roundMaybe Nothing = return Nothing
roundMaybe (Just v) = do
s <- round v
return (Just s)
dl l v = do
x <- roundAll l
return $ v x
@@ -159,14 +175,18 @@ analyze f g i t =
delve (T_IoFile id op file) = d2 op file $ T_IoFile id
delve (T_HereString id word) = d1 word $ T_HereString id
delve (T_FdRedirect id v t) = d1 t $ T_FdRedirect id v
delve (T_Assignment id v t) = d1 t $ T_Assignment id v
delve (T_Assignment id mode var index value) = do
a <- roundMaybe index
b <- round value
return $ T_Assignment id mode var a b
delve (T_Array id t) = dl t $ T_Array id
delve (T_IndexedElement id t1 t2) = d2 t1 t2 $ T_IndexedElement id
delve (T_Redirecting id redirs cmd) = do
a <- roundAll redirs
b <- round cmd
return $ T_Redirecting id a b
delve (T_SimpleCommand id vars cmds) = dll vars cmds $ T_SimpleCommand id
delve (T_Pipeline id l) = dl l $ T_Pipeline id
delve (T_Pipeline id l1 l2) = dll l1 l2 $ T_Pipeline id
delve (T_Banged id l) = d1 l $ T_Banged id
delve (T_AndIf id t u) = d2 t u $ T_AndIf id
delve (T_OrIf id t u) = d2 t u $ T_OrIf id
@@ -185,14 +205,14 @@ analyze f g i t =
delve (T_BraceGroup id l) = dl l $ T_BraceGroup id
delve (T_WhileExpression id c l) = dll c l $ T_WhileExpression id
delve (T_UntilExpression id c l) = dll c l $ T_UntilExpression id
delve (T_ForIn id v w l) = dll w l $ T_ForIn id v
delve (T_ForIn id t v w l) = dll w l $ T_ForIn id t v
delve (T_SelectIn id v w l) = dll w l $ T_SelectIn id v
delve (T_CaseExpression id word cases) = do
newWord <- round word
newCases <- mapM (\(c, t) -> do
newCases <- mapM (\(o, c, t) -> do
x <- mapM round c
y <- mapM round t
return (x,y)
return (o, x,y)
) cases
return $ T_CaseExpression id newWord newCases
@@ -204,10 +224,11 @@ analyze f g i t =
return $ T_ForArithmetic id x y z list
delve (T_Script id s l) = dl l $ T_Script id s
delve (T_Function id name body) = d1 body $ T_Function id name
delve (T_Function id a b name body) = d1 body $ T_Function id a b name
delve (T_Condition id typ token) = d1 token $ T_Condition id typ
delve (T_Extglob id str l) = dl l $ T_Extglob id str
delve (T_DollarBraced id op) = d1 op $ T_DollarBraced id
delve (T_HereDoc id d q str l) = dl l $ T_HereDoc id d q str
delve (TC_And id typ str t1 t2) = d2 t1 t2 $ TC_And id typ str
delve (TC_Or id typ str t1 t2) = d2 t1 t2 $ TC_Or id typ str
@@ -224,8 +245,9 @@ analyze f g i t =
b <- round t2
c <- round t3
return $ TA_Trinary id a b c
delve (TA_Expansion id t) = d1 t $ TA_Expansion id
delve (TA_Base id b t) = d1 t $ TA_Base id b
delve (TA_Expansion id t) = dl t $ TA_Expansion id
delve (TA_Index id t) = d1 t $ TA_Index id
delve (T_Annotation id anns t) = d1 t $ T_Annotation id anns
delve t = return t
getId t = case t of
@@ -272,14 +294,15 @@ getId t = case t of
T_DollarArithmetic id _ -> id
T_BraceExpansion id _ -> id
T_IoFile id _ _ -> id
T_HereDoc id _ _ _ -> id
T_HereDoc id _ _ _ _ -> id
T_HereString id _ -> id
T_FdRedirect id _ _ -> id
T_Assignment id _ _ -> id
T_Assignment id _ _ _ _ -> id
T_Array id _ -> id
T_IndexedElement id _ _ -> id
T_Redirecting id _ _ -> id
T_SimpleCommand id _ _ -> id
T_Pipeline id _ -> id
T_Pipeline id _ _ -> id
T_Banged id _ -> id
T_AndIf id _ _ -> id
T_OrIf id _ _ -> id
@@ -289,10 +312,10 @@ getId t = case t of
T_BraceGroup id _ -> id
T_WhileExpression id _ _ -> id
T_UntilExpression id _ _ -> id
T_ForIn id _ _ _ -> id
T_ForIn id _ _ _ _ -> id
T_SelectIn id _ _ _ -> id
T_CaseExpression id _ _ -> id
T_Function id _ _ -> id
T_Function id _ _ _ _ -> id
T_Arithmetic id _ -> id
T_Script id _ _ -> id
T_Condition id _ _ -> id
@@ -307,21 +330,28 @@ getId t = case t of
TA_Binary id _ _ _ -> id
TA_Unary id _ _ -> id
TA_Sequence id _ -> id
TA_Variable id _ -> id
TA_Trinary id _ _ _ -> id
TA_Expansion id _ -> id
TA_Literal id _ -> id
TA_Base id _ _ -> id
TA_Index id _ -> id
T_ProcSub id _ _ -> id
T_Glob id _ -> id
T_ForArithmetic id _ _ _ _ -> id
T_DollarSingleQuoted id _ -> id
T_DollarDoubleQuoted id _ -> id
T_DollarBracket id _ -> id
T_Annotation id _ _ -> id
T_Pipe id _ -> id
blank :: Monad m => Token -> m ()
blank = const $ return ()
doAnalysis f t = analyze f blank id t
doStackAnalysis startToken endToken t = analyze startToken endToken id t
doTransform i t = runIdentity $ analyze blank blank i t
doAnalysis f = analyze f blank id
doStackAnalysis startToken endToken = analyze startToken endToken id
doTransform i = runIdentity . analyze blank blank i
isLoop t = case t of
T_WhileExpression {} -> True
T_UntilExpression {} -> True
T_ForIn {} -> True
T_ForArithmetic {} -> True
T_SelectIn {} -> True
_ -> False

File diff suppressed because it is too large Load Diff

87
ShellCheck/Data.hs Normal file
View File

@@ -0,0 +1,87 @@
module ShellCheck.Data where
import Data.Version (showVersion)
import Paths_ShellCheck (version)
shellcheckVersion = showVersion version
internalVariables = [
-- Generic
"", "_", "rest", "REST",
-- Bash
"BASH", "BASHOPTS", "BASHPID", "BASH_ALIASES", "BASH_ARGC",
"BASH_ARGV", "BASH_CMDS", "BASH_COMMAND", "BASH_EXECUTION_STRING",
"BASH_LINENO", "BASH_REMATCH", "BASH_SOURCE", "BASH_SUBSHELL",
"BASH_VERSINFO", "BASH_VERSION", "COMP_CWORD", "COMP_KEY",
"COMP_LINE", "COMP_POINT", "COMP_TYPE", "COMP_WORDBREAKS",
"COMP_WORDS", "COPROC", "DIRSTACK", "EUID", "FUNCNAME", "GROUPS",
"HISTCMD", "HOSTNAME", "HOSTTYPE", "LINENO", "MACHTYPE", "MAPFILE",
"OLDPWD", "OPTARG", "OPTIND", "OSTYPE", "PIPESTATUS", "PPID", "PWD",
"RANDOM", "READLINE_LINE", "READLINE_POINT", "REPLY", "SECONDS",
"SHELLOPTS", "SHLVL", "UID", "BASH_ENV", "BASH_XTRACEFD", "CDPATH",
"COLUMNS", "COMPREPLY", "EMACS", "ENV", "FCEDIT", "FIGNORE",
"FUNCNEST", "GLOBIGNORE", "HISTCONTROL", "HISTFILE", "HISTFILESIZE",
"HISTIGNORE", "HISTSIZE", "HISTTIMEFORMAT", "HOME", "HOSTFILE", "IFS",
"IGNOREEOF", "INPUTRC", "LANG", "LC_ALL", "LC_COLLATE", "LC_CTYPE",
"LC_MESSAGES", "LC_NUMERIC", "LINES", "MAIL", "MAILCHECK", "MAILPATH",
"OPTERR", "PATH", "POSIXLY_CORRECT", "PROMPT_COMMAND",
"PROMPT_DIRTRIM", "PS1", "PS2", "PS3", "PS4", "SHELL", "TIMEFORMAT",
"TMOUT", "TMPDIR", "auto_resume", "histchars",
-- Zsh
"ARGV0", "BAUD", "cdpath", "COLUMNS", "CORRECT_IGNORE",
"DIRSTACKSIZE", "ENV", "FCEDIT", "fignore", "fpath", "histchars",
"HISTCHARS", "HISTFILE", "HISTSIZE", "HOME", "IFS", "KEYBOARD_HACK",
"KEYTIMEOUT", "LANG", "LC_ALL", "LC_COLLATE", "LC_CTYPE",
"LC_MESSAGES", "LC_NUMERIC", "LC_TIME", "LINES", "LISTMAX",
"LOGCHECK", "MAIL", "MAILCHECK", "mailpath", "manpath", "module_path",
"NULLCMD", "path", "POSTEDIT", "PROMPT", "PROMPT2", "PROMPT3",
"PROMPT4", "prompt", "PROMPT_EOL_MARK", "PS1", "PS2", "PS3", "PS4",
"psvar", "READNULLCMD", "REPORTTIME", "REPLY", "reply", "RPROMPT",
"RPS1", "RPROMPT2", "RPS2", "SAVEHIST", "SPROMPT", "STTY", "TERM",
"TERMINFO", "TIMEFMT", "TMOUT", "TMPPREFIX", "watch", "WATCHFMT",
"WORDCHARS", "ZBEEP", "ZDOTDIR", "ZLE_LINE_ABORTED",
"ZLE_REMOVE_SUFFIX_CHARS", "ZLE_SPACE_SUFFIX_CHARS"
]
variablesWithoutSpaces = [
"$", "-", "?", "!",
"BASHPID", "BASH_ARGC", "BASH_LINENO", "BASH_SUBSHELL", "EUID", "LINENO",
"OPTIND", "PPID", "RANDOM", "SECONDS", "SHELLOPTS", "SHLVL", "UID",
"COLUMNS", "HISTFILESIZE", "HISTSIZE", "LINES"
]
commonCommands = [
"admin", "alias", "ar", "asa", "at", "awk", "basename", "batch",
"bc", "bg", "break", "c99", "cal", "cat", "cd", "cflow", "chgrp",
"chmod", "chown", "cksum", "cmp", "colon", "comm", "command",
"compress", "continue", "cp", "crontab", "csplit", "ctags", "cut",
"cxref", "date", "dd", "delta", "df", "diff", "dirname", "dot",
"du", "echo", "ed", "env", "eval", "ex", "exec", "exit", "expand",
"export", "expr", "fc", "fg", "file", "find", "fold", "fort77",
"fuser", "gencat", "get", "getconf", "getopts", "grep", "hash",
"head", "iconv", "ipcrm", "ipcs", "jobs", "join", "kill", "lex",
"link", "ln", "locale", "localedef", "logger", "logname", "lp",
"ls", "m4", "mailx", "make", "man", "mesg", "mkdir", "mkfifo",
"more", "mv", "newgrp", "nice", "nl", "nm", "nohup", "od", "paste",
"patch", "pathchk", "pax", "pr", "printf", "prs", "ps", "pwd",
"qalter", "qdel", "qhold", "qmove", "qmsg", "qrerun", "qrls",
"qselect", "qsig", "qstat", "qsub", "read", "readonly", "renice",
"return", "rm", "rmdel", "rmdir", "sact", "sccs", "sed", "set",
"sh", "shift", "sleep", "sort", "split", "strings", "strip", "stty",
"tabs", "tail", "talk", "tee", "test", "time", "times", "touch",
"tput", "tr", "trap", "tsort", "tty", "type", "ulimit", "umask",
"unalias", "uname", "uncompress", "unexpand", "unget", "uniq",
"unlink", "unset", "uucp", "uudecode", "uuencode", "uustat", "uux",
"val", "vi", "wait", "wc", "what", "who", "write", "xargs", "yacc",
"zcat"
]
sampleWords = [
"alpha", "bravo", "charlie", "delta", "echo", "foxtrot",
"golf", "hotel", "india", "juliett", "kilo", "lima", "mike",
"november", "oscar", "papa", "quebec", "romeo", "sierra",
"tango", "uniform", "victor", "whiskey", "xray", "yankee",
"zulu"
]

14
ShellCheck/Options.hs Normal file
View File

@@ -0,0 +1,14 @@
module ShellCheck.Options where
data Shell = Ksh | Zsh | Sh | Bash
deriving (Show, Eq)
data AnalysisOptions = AnalysisOptions {
optionShellType :: Maybe Shell,
optionExcludes :: [Integer]
}
defaultAnalysisOptions = AnalysisOptions {
optionShellType = Nothing,
optionExcludes = []
}

File diff suppressed because it is too large Load Diff

View File

@@ -15,29 +15,32 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-}
module ShellCheck.Simple (shellCheck, ShellCheckComment, scLine, scColumn, scSeverity, scMessage) where
{-# LANGUAGE TemplateHaskell #-}
module ShellCheck.Simple (shellCheck, ShellCheckComment, scLine, scColumn, scSeverity, scCode, scMessage, runTests) where
import ShellCheck.Parser
import ShellCheck.Analytics
import Data.Maybe
import Text.Parsec.Pos
import Data.List
import Data.Maybe
import ShellCheck.Analytics hiding (runTests)
import ShellCheck.Options
import ShellCheck.Parser hiding (runTests)
import Test.QuickCheck.All (quickCheckAll)
import Text.Parsec.Pos
shellCheck :: String -> [ShellCheckComment]
shellCheck script =
shellCheck :: AnalysisOptions -> String -> [ShellCheckComment]
shellCheck options script =
let (ParseResult result notes) = parseShell "-" script in
let allNotes = notes ++ (concat $ maybeToList $ do
(tree, map) <- result
let newMap = runAllAnalytics tree map
return $ notesFromMap newMap
let allNotes = notes ++ concat (maybeToList $ do
(tree, posMap) <- result
let list = runAnalytics options tree
return $ map (noteToParseNote posMap) $ filterByAnnotation tree list
)
in
map formatNote $ nub $ sortNotes allNotes
data ShellCheckComment = ShellCheckComment { scLine :: Int, scColumn :: Int, scSeverity :: String, scMessage :: String }
data ShellCheckComment = ShellCheckComment { scLine :: Int, scColumn :: Int, scSeverity :: String, scCode :: Int, scMessage :: String }
instance Show ShellCheckComment where
show c = concat ["(", show $ scLine c, ",", show $ scColumn c, ") ", scSeverity c, ": ", scMessage c]
show c = concat ["(", show $ scLine c, ",", show $ scColumn c, ") ", scSeverity c, ": ", show (scCode c), " ", scMessage c]
severityToString s =
case s of
@@ -46,4 +49,29 @@ severityToString s =
InfoC -> "info"
StyleC -> "style"
formatNote (ParseNote pos severity text) = ShellCheckComment (sourceLine pos) (sourceColumn pos) (severityToString severity) text
formatNote (ParseNote pos severity code text) =
ShellCheckComment (sourceLine pos) (sourceColumn pos) (severityToString severity) (fromIntegral code) text
testCheck = shellCheck defaultAnalysisOptions { optionExcludes = [2148] } -- Ignore #! warnings
prop_findsParseIssue =
let comments = testCheck "echo \"$12\"" in
length comments == 1 && scCode (head comments) == 1037
prop_commentDisablesParseIssue1 =
null $ testCheck "#shellcheck disable=SC1037\necho \"$12\""
prop_commentDisablesParseIssue2 =
null $ testCheck "#shellcheck disable=SC1037\n#lol\necho \"$12\""
prop_findsAnalysisIssue =
let comments = testCheck "echo $1" in
length comments == 1 && scCode (head comments) == 2086
prop_commentDisablesAnalysisIssue1 =
null $ testCheck "#shellcheck disable=SC2086\necho $1"
prop_commentDisablesAnalysisIssue2 =
null $ testCheck "#shellcheck disable=SC2086\n#lol\necho $1"
prop_optionDisablesIssue1 =
null $ shellCheck (defaultAnalysisOptions { optionExcludes = [2086, 2148] }) "echo $1"
return []
runTests = $quickCheckAll

View File

@@ -1,32 +0,0 @@
{-
This file is part of ShellCheck.
http://www.vidarholen.net/contents/shellcheck
ShellCheck is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ShellCheck is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-}
import ShellCheck.Simple
import Text.JSON
instance JSON ShellCheckComment where
showJSON c = makeObj [
("line", showJSON $ scLine c),
("column", showJSON $ scColumn c),
("level", showJSON $ scSeverity c),
("message", showJSON $ scMessage c)
]
readJSON = undefined
main = do
script <- getContents
putStrLn $ encodeStrict $ shellCheck script

125
shellcheck.1.md Normal file
View File

@@ -0,0 +1,125 @@
% SHELLCHECK(1) Shell script analysis tool
# NAME
shellcheck - Shell script analysis tool
# SYNOPSIS
**shellcheck** [*OPTIONS*...] *FILES*...
# DESCRIPTION
ShellCheck is a static analysis and linting tool for sh/bash scripts. It's
mainly focused on handling typical beginner and intermediate level syntax
errors and pitfalls where the shell just gives a cryptic error message or
strange behavior, but it also reports on a few more advanced issues where
corner cases can cause delayed failures.
# OPTIONS
**-e**\ *CODE1*[,*CODE2*...],\ **--exclude=***CODE1*[,*CODE2*...]
: Explicitly exclude the specified codes from the report. Subsequent **-e**
options are cumulative, but all the codes can be specified at once,
comma-separated as a single argument.
**-f** *FORMAT*, **--format=***FORMAT*
: Specify the output format of shellcheck, which prints its results in the
standard output. Subsequent **-f** options are ignored, see **FORMATS**
below for more information.
**-s**\ *shell*,\ **--shell=***shell*
: Specify Bourne shell dialect. Valid values are *sh*, *bash*, *ksh* and
*zsh*. The default is to use the file's shebang, or *bash* if the target
shell can't be determined.
**-V**\ *version*,\ **--version**
: Print version and exit.
# FORMATS
**tty**
: Plain text, human readable output. This is the default.
**gcc**
: GCC compatible output. Useful for editors that support compiling and
showing syntax errors.
For example, in Vim, `:set makeprg=shellcheck\ -f\ gcc\ %` will allow
using `:make` to check the script, and `:cnext` to jump to the next error.
<file>:<line>:<column>: <type>: <message>
**checkstyle**
: Checkstyle compatible XML output. Supported directly or through plugins
by many IDEs and build monitoring systems.
<?xml version='1.0' encoding='UTF-8'?>
<checkstyle version='4.3'>
<file name='file'>
<error
line='line'
column='column'
severity='severity'
message='message'
source='ShellCheck.SC####' />
...
</file>
...
</checkstyle>
**json**
: Json is a popular serialization format that is more suitable for web
applications. ShellCheck's json is compact and contains only the bare
minimum.
[
{
"line": line,
"column": column,
"level": level,
"code": ####,
"message": message
},
...
]
# DIRECTIVES
ShellCheck directives can be specified as comments in the shell script
before a command or block:
# shellcheck key=value key=value
command-or-structure
For example, to suppress SC2035 about using `./*.jpg`:
# shellcheck disable=SC2035
echo "Files: " *.jpg
Valid keys are:
**disable**
: Disables a comma separated list of error codes for the following command.
The command can be a simple command like `echo foo`, or a compound command
like a function definition, subshell block or loop.
# AUTHOR
ShellCheck is written and maintained by Vidar Holen.
# REPORTING BUGS
Bugs and issues can be reported on GitHub:
https://github.com/koalaman/shellcheck/issues
# SEE ALSO
sh(1) bash(1)

View File

@@ -15,72 +15,334 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-}
import Control.Exception
import Control.Monad
import Control.Monad.Trans
import Control.Monad.Trans.Error
import Data.Char
import Data.Maybe
import Data.Monoid
import GHC.Exts
import GHC.IO.Device
import Prelude hiding (catch)
import ShellCheck.Data
import ShellCheck.Options
import ShellCheck.Simple
import ShellCheck.Analytics
import System.Console.GetOpt
import System.Directory
import System.Environment
import System.Exit
import System.IO
import Text.JSON
import qualified Data.Map as Map
data Flag = Flag String String
data Status = NoProblems | SomeProblems | BadInput | SupportFailure | SyntaxFailure | RuntimeException deriving (Ord, Eq)
instance Error Status where
noMsg = RuntimeException
instance Monoid Status where
mempty = NoProblems
mappend = max
header = "Usage: shellcheck [OPTIONS...] FILES..."
options = [
Option "e" ["exclude"]
(ReqArg (Flag "exclude") "CODE1,CODE2..") "exclude types of warnings",
Option "f" ["format"]
(ReqArg (Flag "format") "FORMAT") "output format",
Option "s" ["shell"]
(ReqArg (Flag "shell") "SHELLNAME") "Specify dialect (bash,sh,ksh,zsh)",
Option "V" ["version"]
(NoArg $ Flag "version" "true") "Print version information"
]
printErr = hPutStrLn stderr
clear = ansi 0
ansi n = "\x1B[" ++ (show n) ++ "m"
instance JSON ShellCheckComment where
showJSON c = makeObj [
("line", showJSON $ scLine c),
("column", showJSON $ scColumn c),
("level", showJSON $ scSeverity c),
("code", showJSON $ scCode c),
("message", showJSON $ scMessage c)
]
readJSON = undefined
colorForLevel "error" = 31 -- red
colorForLevel "warning" = 33 -- yellow
colorForLevel "info" = 32 -- green
colorForLevel "style" = 32 -- green
colorForLevel "message" = 1 -- bold
colorForLevel "source" = 0 -- none
colorForLevel _ = 0 -- none
parseArguments :: [String] -> ErrorT Status IO ([Flag], [FilePath])
parseArguments argv =
case getOpt Permute options argv of
(opts, files, []) -> return (opts, files)
(_, _, errors) -> do
liftIO . printErr $ concat errors ++ "\n" ++ usageInfo header options
throwError SyntaxFailure
colorComment level comment = (ansi $ colorForLevel level) ++ comment ++ clear
formats :: Map.Map String (AnalysisOptions -> [FilePath] -> IO Status)
formats = Map.fromList [
("json", forJson),
("gcc", forGcc),
("checkstyle", forCheckstyle),
("tty", forTty)
]
doFile path colorFunc = do
let actualPath = if path == "-" then "/dev/stdin" else path
exists <- doesFileExist actualPath
if exists then do
contents <- readFile actualPath
doInput path contents colorFunc
else do
putStrLn (colorFunc "error" $ "No such file: " ++ actualPath)
toStatus = liftM (either id (const NoProblems)) . runErrorT
doInput filename contents colorFunc = do
let fileLines = lines contents
let lineCount = length fileLines
let comments = shellCheck contents
let groups = groupWith scLine comments
if not $ null comments then do
catchExceptions :: IO Status -> IO Status
catchExceptions action = action -- action `catch` handler
where
handler err = do
printErr $ show (err :: SomeException)
return RuntimeException
checkComments comments = if null comments then NoProblems else SomeProblems
forTty :: AnalysisOptions -> [FilePath] -> IO Status
forTty options files = do
output <- mapM doFile files
return $ mconcat output
where
clear = ansi 0
ansi n = "\x1B[" ++ show n ++ "m"
colorForLevel "error" = 31 -- red
colorForLevel "warning" = 33 -- yellow
colorForLevel "info" = 32 -- green
colorForLevel "style" = 32 -- green
colorForLevel "message" = 1 -- bold
colorForLevel "source" = 0 -- none
colorForLevel _ = 0 -- none
colorComment level comment =
ansi (colorForLevel level) ++ comment ++ clear
doFile path = catchExceptions $ do
contents <- readContents path
doInput path contents
doInput filename contents = do
let fileLines = lines contents
let lineCount = length fileLines
let comments = getComments options contents
let groups = groupWith scLine comments
colorFunc <- getColorFunc
mapM_ (\x -> do
let lineNum = scLine (head x)
let line = if lineNum < 1 || lineNum > lineCount
then ""
else fileLines !! (lineNum - 1)
putStrLn ""
putStrLn $ colorFunc "message" ("In " ++ filename ++" line " ++ (show $ lineNum) ++ ":")
putStrLn $ colorFunc "message"
("In " ++ filename ++" line " ++ show lineNum ++ ":")
putStrLn (colorFunc "source" line)
mapM (\c -> putStrLn (colorFunc (scSeverity c) $ cuteIndent c)) x
mapM_ (\c -> putStrLn (colorFunc (scSeverity c) $ cuteIndent c)) x
putStrLn ""
) groups
else do
putStrLn ("No comments for " ++ filename)
return . checkComments $ comments
cuteIndent comment =
(replicate ((scColumn comment) - 1) ' ') ++ "^-- " ++ (scMessage comment)
cuteIndent comment =
replicate (scColumn comment - 1) ' ' ++
"^-- " ++ code (scCode comment) ++ ": " ++ scMessage comment
getColorFunc = do
term <- hIsTerminalDevice stdout
return $ if term then colorComment else const id
code code = "SC" ++ show code
getColorFunc = do
term <- hIsTerminalDevice stdout
return $ if term then colorComment else const id
-- This totally ignores the filenames. Fixme?
forJson :: AnalysisOptions -> [FilePath] -> IO Status
forJson options files = catchExceptions $ do
comments <- liftM concat $ mapM (commentsFor options) files
putStrLn $ encodeStrict comments
return $ checkComments comments
-- Mimic GCC "file:line:col: (error|warning|note): message" format
forGcc :: AnalysisOptions -> [FilePath] -> IO Status
forGcc options files = do
files <- mapM process files
return $ mconcat files
where
process file = catchExceptions $ do
contents <- readContents file
let comments = makeNonVirtual (getComments options contents) contents
mapM_ (putStrLn . format file) comments
return $ checkComments comments
format filename c = concat [
filename, ":",
show $ scLine c, ":",
show $ scColumn c, ": ",
case scSeverity c of
"error" -> "error"
"warning" -> "warning"
_ -> "note",
": ",
concat . lines $ scMessage c,
" [SC", show $ scCode c, "]"
]
-- Checkstyle compatible output. A bit of a hack to avoid XML dependencies
forCheckstyle :: AnalysisOptions -> [FilePath] -> IO Status
forCheckstyle options files = do
putStrLn "<?xml version='1.0' encoding='UTF-8'?>"
putStrLn "<checkstyle version='4.3'>"
statuses <- mapM process files
putStrLn "</checkstyle>"
return $ mconcat statuses
where
process file = catchExceptions $ do
comments <- commentsFor options file
putStrLn (formatFile file comments)
return $ checkComments comments
severity "error" = "error"
severity "warning" = "warning"
severity _ = "info"
attr s v = concat [ s, "='", escape v, "' " ]
escape = concatMap escape'
escape' c = if isOk c then [c] else "&#" ++ show (ord c) ++ ";"
isOk x = any ($x) [isAsciiUpper, isAsciiLower, isDigit, (`elem` " ./")]
formatFile name comments = concat [
"<file ", attr "name" name, ">\n",
concatMap format comments,
"</file>"
]
format c = concat [
"<error ",
attr "line" $ show . scLine $ c,
attr "column" $ show . scColumn $ c,
attr "severity" $ severity . scSeverity $ c,
attr "message" $ scMessage c,
attr "source" $ "ShellCheck.SC" ++ show (scCode c),
"/>\n"
]
commentsFor options file = liftM (getComments options) $ readContents file
getComments = shellCheck
readContents :: FilePath -> IO String
readContents file =
if file == "-"
then getContents
else readFile file
-- Realign comments from a tabstop of 8 to 1
makeNonVirtual comments contents =
map fix comments
where
ls = lines contents
fix c = c {
scColumn =
if scLine c > 0 && scLine c <= length ls
then real (ls !! (scLine c - 1)) 0 0 (scColumn c)
else scColumn c
}
real _ r v target | target <= v = r
real [] r v _ = r -- should never happen
real ('\t':rest) r v target =
real rest (r+1) (v + 8 - (v `mod` 8)) target
real (_:rest) r v target = real rest (r+1) (v+1) target
getOption [] _ = Nothing
getOption (Flag var val:_) name | name == var = return val
getOption (_:rest) flag = getOption rest flag
getOptions options name =
map (\(Flag _ val) -> val) . filter (\(Flag var _) -> var == name) $ options
split char str =
split' str []
where
split' (a:rest) element =
if a == char
then reverse element : split' rest []
else split' rest (a:element)
split' [] element = [reverse element]
getExclusions options =
let elements = concatMap (split ',') $ getOptions options "exclude"
clean = dropWhile (not . isDigit)
in
map (Prelude.read . clean) elements :: [Int]
excludeCodes codes =
filter (not . hasCode)
where
hasCode c = scCode c `elem` codes
main = do
args <- getArgs
colors <- getColorFunc
if null args then do
hPutStrLn stderr "shellcheck -- bash/sh script static analysis tool"
hPutStrLn stderr "Usage: shellcheck filenames..."
exitFailure
else
mapM (\f -> doFile f colors) args
status <- toStatus $ do
(flags, files) <- parseArguments args
process flags files
exitWith $ statusToCode status
statusToCode status =
case status of
NoProblems -> ExitSuccess
SomeProblems -> ExitFailure 1
BadInput -> ExitFailure 5
SyntaxFailure -> ExitFailure 3
SupportFailure -> ExitFailure 4
RuntimeException -> ExitFailure 2
process :: [Flag] -> [FilePath] -> ErrorT Status IO ()
process flags files = do
options <- foldM (flip parseOption) defaultAnalysisOptions flags
verifyFiles files
let format = fromMaybe "tty" $ getOption flags "format"
case Map.lookup format formats of
Nothing -> do
liftIO $ do
printErr $ "Unknown format " ++ format
printErr "Supported formats:"
mapM_ (printErr . write) $ Map.keys formats
throwError SupportFailure
where write s = " " ++ s
Just f -> ErrorT $ liftM Left $ f options files
parseOption flag options =
case flag of
Flag "shell" str ->
fromMaybe (die $ "Unknown shell: " ++ str) $ do
shell <- shellForExecutable str
return $ return options { optionShellType = Just shell }
Flag "exclude" str -> do
new <- mapM parseNum $ split ',' str
let old = optionExcludes options
return options { optionExcludes = new ++ old }
Flag "version" _ -> do
liftIO printVersion
throwError NoProblems
_ -> return options
where
die s = do
liftIO $ printErr s
throwError SupportFailure
parseNum ('S':'C':str) = parseNum str
parseNum num = do
unless (all isDigit num) $ do
liftIO . printErr $ "Bad exclusion: " ++ num
throwError SyntaxFailure
return (Prelude.read num :: Integer)
verifyFiles files =
when (null files) $ do
liftIO $ printErr "No files specified.\n"
liftIO $ printErr $ usageInfo header options
throwError SyntaxFailure
printVersion = do
putStrLn "ShellCheck - shell script analysis tool"
putStrLn $ "version: " ++ shellcheckVersion
putStrLn "license: GNU Affero General Public License, version 3"
putStrLn "website: http://www.shellcheck.net"

View File

@@ -1,65 +0,0 @@
#!/usr/bin/env runhaskell
-- #!/usr/bin/env runhugs
-- $Id: quickcheck,v 1.4 2003/01/08 15:09:22 shae Exp $
-- This file defines a command
-- quickCheck <options> <files>
-- which invokes quickCheck on all properties defined in the files given as
-- arguments, by generating an input script for hugs and then invoking it.
-- quickCheck recognises the options
-- +names print the name of each property before checking it
-- -names do not print property names (the default)
-- +verbose displays each test case before running
-- -verbose do not displays each test case before running (the default)
-- Other options (beginning with + or -) are passed unchanged to hugs.
--
-- Change the first line of this file to the location of runhaskell or runhugs
-- on your system.
-- Make the file executable.
--
-- TODO:
-- someone on #haskell asked about supporting QC tests inside LaTeX, ex. \{begin} \{end}, how?
import System.Cmd
import System.Directory (findExecutable)
import System.Environment
import Data.List
import Data.Maybe (fromJust)
main :: IO ()
main = do as<-getArgs
sequence_ (map (process (filter isOption as))
(filter (not.isOption) as))
-- ugly hack for .lhs files, is there a better way?
unlit [] = []
unlit x = if (head x) == '>' then (tail x) else x
process opts file =
let (namesOpt,opts') = getOption "names" "-names" opts
(verboseOpt,opts'') = getOption "verbose" "-verbose" opts' in
do xs<-readFile file
let names = nub$ filter (\x -> (("> prop_" `isPrefixOf` x) || ("prop_" `isPrefixOf` x)))
(map (fst.head.lex.unlit) (lines xs))
if null names then
putStr (file++": no properties to check\n")
else do writeFile "hugsin"$
unlines ((":load "++file):
":m +Test.QuickCheck":
"let quackCheck p = quickCheckWith (stdArgs { maxSuccess = 1 }) p ":
[(if namesOpt=="+names" then
"putStr \""++p++": \" >> "
else "") ++
("quackCheck ")
++ p | p<-names])
-- To use ghci
ghci <- findExecutable "ghci"
system (fromJust ghci ++options opts''++" <hugsin")
return ()
isOption xs = head xs `elem` "-+"
options opts = unwords ["\""++opt++"\"" | opt<-opts]
getOption name def opts =
let opt = head [opt | opt<-opts++[def], isPrefixOf name (drop 1 opt)] in
(opt, filter (/=opt) opts)

View File

@@ -1,22 +0,0 @@
#!/bin/bash
# Todo: Find a way to make this not suck.
ulimit -t 60 # Sometimes GHC ends in a spin loop, and this is easier than debugging
[[ -e test/quackCheck.hs ]] || { echo "Are you running me from the wrong directory?"; exit 1; }
[[ $1 == -v ]] && pattern="" || pattern="FAIL"
find . -name '*.hs' -exec bash -c '
grep -v "^module " "$1" > quack.tmp.hs
./test/quackCheck.hs +names quack.tmp.hs
' -- {} \; 2>&1 | grep -i "$pattern"
result=$?
rm -f quack.tmp.hs hugsin
if [[ $result == 0 ]]
then
exit 1
else
exit 0
fi

16
test/shellcheck.hs Normal file
View File

@@ -0,0 +1,16 @@
module Main where
import Control.Monad
import System.Exit
import qualified ShellCheck.Simple
import qualified ShellCheck.Analytics
import qualified ShellCheck.Parser
main = do
putStrLn "Running ShellCheck tests..."
results <- sequence [ShellCheck.Simple.runTests,
ShellCheck.Analytics.runTests,
ShellCheck.Parser.runTests]
if and results then exitSuccess
else exitFailure