mirror of
https://github.com/koalaman/shellcheck.git
synced 2025-09-30 00:39:19 +08:00
Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
4f5dc7094b |
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: Build ShellCheck
|
name: Build Lol
|
||||||
|
|
||||||
# Run this workflow every time a new commit pushed to your repository
|
# Run this workflow every time a new commit pushed to your repository
|
||||||
on: push
|
on: push
|
||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
mkdir source
|
mkdir source
|
||||||
cabal sdist
|
cabal sdist
|
||||||
mv dist-newstyle/sdist/*.tar.gz source/source.tar.gz
|
mv dist/*.tar.gz source/source.tar.gz
|
||||||
|
|
||||||
- name: Deduce tags
|
- name: Deduce tags
|
||||||
run: |
|
run: |
|
||||||
|
11
CHANGELOG.md
11
CHANGELOG.md
@@ -1,14 +1,13 @@
|
|||||||
## v0.7.2 - 2021-04-19
|
## Git
|
||||||
### Added
|
### Added
|
||||||
- `disable` directives can now be a range, e.g. `disable=SC3000-SC4000`
|
- `disable` directives can now be a range, e.g. `disable=SC3000-SC4000`
|
||||||
- SC1143: Warn about line continuations in comments
|
|
||||||
- SC2259/SC2260: Warn when redirections override pipes
|
- SC2259/SC2260: Warn when redirections override pipes
|
||||||
- SC2261: Warn about multiple competing redirections
|
- SC2261: Warn about multiple competing redirections
|
||||||
- SC2262/SC2263: Warn about aliases declared and used in the same parsing unit
|
- SC2262/SC2263: Warn about aliases declared and used in the same parsing unit
|
||||||
- SC2264: Warn about wrapper functions that blatantly recurse
|
- SC2264: Warn about wrapper functions that blatantly recurse
|
||||||
- SC2265/SC2266: Warn when using & or | with test statements
|
- SC2265/SC2266: Warn when using & or | with test statements
|
||||||
- SC2267: Warn when using xargs -i instead of -I
|
- SC2267: Warn when using xargs -i instead of -I
|
||||||
- SC2268: Warn about unnecessary x-comparisons like `[ x$var = xval ]`
|
- Optional avoid-x-comparisons: Style warning SC2268 for `[ x$var = xval ]`
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- SC1072/SC1073 now respond to disable annotations, though ignoring parse errors
|
- SC1072/SC1073 now respond to disable annotations, though ignoring parse errors
|
||||||
@@ -22,7 +21,7 @@
|
|||||||
- POSIX/dash unsupported feature warnings now have individual SC3xxx codes
|
- POSIX/dash unsupported feature warnings now have individual SC3xxx codes
|
||||||
- SC1090: A leading `$x/` or `$(x)/` is now treated as `./` when locating files
|
- SC1090: A leading `$x/` or `$(x)/` is now treated as `./` when locating files
|
||||||
- SC2154: Variables appearing in -z/-n tests are no longer considered unassigned
|
- SC2154: Variables appearing in -z/-n tests are no longer considered unassigned
|
||||||
- SC2270-SC2285: Improved warnings about misused `=`, e.g. `${var}=42`
|
- SC2270-SC2285: Improved warnings about misused =, e.g. `${var}=42`
|
||||||
|
|
||||||
|
|
||||||
## v0.7.1 - 2020-04-04
|
## v0.7.1 - 2020-04-04
|
||||||
@@ -165,7 +164,7 @@
|
|||||||
- SC2204/SC2205: Warn about `( -z foo )` and `( foo -eq bar )`
|
- SC2204/SC2205: Warn about `( -z foo )` and `( foo -eq bar )`
|
||||||
- SC2200/SC2201: Warn about brace expansion in [/[[
|
- SC2200/SC2201: Warn about brace expansion in [/[[
|
||||||
- SC2198/SC2199: Warn about arrays in [/[[
|
- SC2198/SC2199: Warn about arrays in [/[[
|
||||||
- SC2196/SC2197: Warn about deprecated egrep/fgrep
|
- SC2196/SC2197: Warn about deprected egrep/fgrep
|
||||||
- SC2195: Warn about unmatchable case branches
|
- SC2195: Warn about unmatchable case branches
|
||||||
- SC2194: Warn about constant 'case' statements
|
- SC2194: Warn about constant 'case' statements
|
||||||
- SC2193: Warn about `[[ file.png == *.mp3 ]]` and other unmatchables
|
- SC2193: Warn about `[[ file.png == *.mp3 ]]` and other unmatchables
|
||||||
@@ -182,7 +181,7 @@
|
|||||||
### Fixed
|
### Fixed
|
||||||
- `-c` no longer suggested when using `grep -o | wc`
|
- `-c` no longer suggested when using `grep -o | wc`
|
||||||
- Comments and whitespace are now allowed before filewide directives
|
- Comments and whitespace are now allowed before filewide directives
|
||||||
- Here doc delimiters with esoteric quoting like `foo""` are now handled
|
- Here doc delimters with esoteric quoting like `foo""` are now handled
|
||||||
- SC2095 about `ssh` in while read loops is now suppressed when using `-n`
|
- SC2095 about `ssh` in while read loops is now suppressed when using `-n`
|
||||||
- `%(%Y%M%D)T` now recognized as a single formatter in `printf` checks
|
- `%(%Y%M%D)T` now recognized as a single formatter in `printf` checks
|
||||||
- `grep -F` now suppresses regex related suggestions
|
- `grep -F` now suppresses regex related suggestions
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
Name: ShellCheck
|
Name: ShellCheck
|
||||||
Version: 0.7.2
|
Version: 0.7.1
|
||||||
Synopsis: Shell script analysis tool
|
Synopsis: Shell script analysis tool
|
||||||
License: GPL-3
|
License: GPL-3
|
||||||
License-file: LICENSE
|
License-file: LICENSE
|
||||||
|
@@ -22,7 +22,7 @@ RUN curl -L "https://downloads.haskell.org/~cabal/cabal-install-3.2.0.0/cabal-in
|
|||||||
ENV CABALOPTS "--with-ghc=$TARGET-ghc;--with-hc-pkg=$TARGET-ghc-pkg"
|
ENV CABALOPTS "--with-ghc=$TARGET-ghc;--with-hc-pkg=$TARGET-ghc-pkg"
|
||||||
|
|
||||||
# Prebuild the dependencies
|
# Prebuild the dependencies
|
||||||
RUN cabal update && IFS=';' && cabal install --dependencies-only $CABALOPTS ShellCheck
|
RUN cabal update && IFS=';' && cabal install $CABALOPTS --lib Diff-0.4.0 base-compat-0.11.2 base-orphans-0.8.4 dlist-1.0 hashable-1.3.0.0 indexed-traversable-0.1.1 integer-logarithms-1.0.3.1 primitive-0.7.1.0 regex-base-0.94.0.0 splitmix-0.1.0.3 tagged-0.8.6.1 th-abstraction-0.4.2.0 transformers-compat-0.6.6 base-compat-batteries-0.11.2 time-compat-1.9.5 unordered-containers-0.2.13.0 data-fix-0.3.1 vector-0.12.2.0 scientific-0.3.6.2 regex-tdfa-1.3.1.0 random-1.2.0 distributive-0.6.2.1 attoparsec-0.13.2.5 uuid-types-1.0.3 comonad-5.0.8 bifunctors-5.5.10 assoc-1.0.2 these-1.1.1.1 strict-0.4.0.1 aeson-1.5.5.1
|
||||||
|
|
||||||
# Copy the build script
|
# Copy the build script
|
||||||
COPY build /usr/bin
|
COPY build /usr/bin
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
set -xe
|
set -xe
|
||||||
{
|
{
|
||||||
tar xzv --strip-components=1
|
tar xzv --strip-components=1
|
||||||
chmod +x striptests && ./striptests
|
./striptests
|
||||||
mkdir "$TARGETNAME"
|
mkdir "$TARGETNAME"
|
||||||
cabal update
|
cabal update
|
||||||
( IFS=';'; cabal build $CABALOPTS )
|
( IFS=';'; cabal build $CABALOPTS )
|
||||||
|
@@ -21,7 +21,7 @@ RUN curl -L "https://downloads.haskell.org/~cabal/cabal-install-3.2.0.0/cabal-in
|
|||||||
ENV CABALOPTS "--ghc-options;-split-sections -optc-Os -optc-Wl,--gc-sections;--with-ghc=$TARGET-ghc;--with-hc-pkg=$TARGET-ghc-pkg"
|
ENV CABALOPTS "--ghc-options;-split-sections -optc-Os -optc-Wl,--gc-sections;--with-ghc=$TARGET-ghc;--with-hc-pkg=$TARGET-ghc-pkg"
|
||||||
|
|
||||||
# Prebuild the dependencies
|
# Prebuild the dependencies
|
||||||
RUN cabal update && IFS=';' && cabal install --dependencies-only $CABALOPTS ShellCheck
|
RUN cabal update && IFS=';' && cabal install $CABALOPTS --lib Diff-0.4.0 base-compat-0.11.2 base-orphans-0.8.4 dlist-1.0 hashable-1.3.0.0 indexed-traversable-0.1.1 integer-logarithms-1.0.3.1 primitive-0.7.1.0 regex-base-0.94.0.0 splitmix-0.1.0.3 tagged-0.8.6.1 th-abstraction-0.4.2.0 transformers-compat-0.6.6 base-compat-batteries-0.11.2 time-compat-1.9.5 unordered-containers-0.2.13.0 data-fix-0.3.1 vector-0.12.2.0 scientific-0.3.6.2 regex-tdfa-1.3.1.0 random-1.2.0 distributive-0.6.2.1 attoparsec-0.13.2.5 uuid-types-1.0.3 comonad-5.0.8 bifunctors-5.5.10 assoc-1.0.2 these-1.1.1.1 strict-0.4.0.1 aeson-1.5.5.1
|
||||||
|
|
||||||
# Copy the build script
|
# Copy the build script
|
||||||
COPY build /usr/bin
|
COPY build /usr/bin
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
set -xe
|
set -xe
|
||||||
{
|
{
|
||||||
tar xzv --strip-components=1
|
tar xzv --strip-components=1
|
||||||
chmod +x striptests && ./striptests
|
./striptests
|
||||||
mkdir "$TARGETNAME"
|
mkdir "$TARGETNAME"
|
||||||
cabal update
|
cabal update
|
||||||
( IFS=';'; cabal build $CABALOPTS --enable-executable-static )
|
( IFS=';'; cabal build $CABALOPTS --enable-executable-static )
|
||||||
|
@@ -51,7 +51,7 @@ RUN pirun apt-get install -y ghc cabal-install
|
|||||||
# Finally we can build the current dependencies. This takes hours.
|
# Finally we can build the current dependencies. This takes hours.
|
||||||
ENV CABALOPTS "--ghc-options;-split-sections -optc-Os -optc-Wl,--gc-sections;--gcc-options;-Os -Wl,--gc-sections -ffunction-sections -fdata-sections"
|
ENV CABALOPTS "--ghc-options;-split-sections -optc-Os -optc-Wl,--gc-sections;--gcc-options;-Os -Wl,--gc-sections -ffunction-sections -fdata-sections"
|
||||||
RUN pirun cabal update
|
RUN pirun cabal update
|
||||||
RUN IFS=";" && pirun cabal install --dependencies-only $CABALOPTS ShellCheck
|
RUN IFS=";" && pirun cabal install --lib $CABALOPTS Diff-0.4.0 base-compat-0.11.2 base-orphans-0.8.4 dlist-1.0 hashable-1.3.1.0 indexed-traversable-0.1.1 integer-logarithms-1.0.3.1 primitive-0.7.1.0 regex-base-0.94.0.1 splitmix-0.1.0.3 tagged-0.8.6.1 th-abstraction-0.4.2.0 transformers-compat-0.6.6 base-compat-batteries-0.11.2 time-compat-1.9.5 unordered-containers-0.2.13.0 data-fix-0.3.1 vector-0.12.2.0 scientific-0.3.6.2 regex-tdfa-1.3.1.0 random-1.2.0 distributive-0.6.2.1 attoparsec-0.13.2.5 uuid-types-1.0.4 comonad-5.0.8 bifunctors-5.5.10 assoc-1.0.2 these-1.1.1.1 strict-0.4.0.1 aeson-1.5.6.0
|
||||||
|
|
||||||
# Copy the build script
|
# Copy the build script
|
||||||
WORKDIR /pi/scratch
|
WORKDIR /pi/scratch
|
||||||
|
@@ -3,7 +3,7 @@ set -xe
|
|||||||
cd /scratch
|
cd /scratch
|
||||||
{
|
{
|
||||||
tar xzv --strip-components=1
|
tar xzv --strip-components=1
|
||||||
chmod +x striptests && ./striptests
|
./striptests
|
||||||
mkdir "$TARGETNAME"
|
mkdir "$TARGETNAME"
|
||||||
# This script does not cabal update because compiling anything new is slow
|
# This script does not cabal update because compiling anything new is slow
|
||||||
( IFS=';'; cabal build $CABALOPTS --enable-executable-static )
|
( IFS=';'; cabal build $CABALOPTS --enable-executable-static )
|
||||||
|
30
build/linux.ppc64le/Dockerfile
Normal file
30
build/linux.ppc64le/Dockerfile
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
FROM ubuntu:20.04
|
||||||
|
|
||||||
|
ENV TARGET powerpc64le-linux-gnu
|
||||||
|
ENV TARGETNAME linux.ppc64le
|
||||||
|
|
||||||
|
# Build dependencies
|
||||||
|
USER root
|
||||||
|
ENV DEBIAN_FRONTEND noninteractive
|
||||||
|
RUN apt-get update && apt-get install -y ghc automake autoconf build-essential llvm curl qemu-user-static gcc-$TARGET
|
||||||
|
|
||||||
|
# Build GHC
|
||||||
|
WORKDIR /ghc
|
||||||
|
RUN curl -L "https://downloads.haskell.org/~ghc/8.10.4/ghc-8.10.4-src.tar.xz" | tar xJ --strip-components=1
|
||||||
|
RUN ./boot && ./configure --host x86_64-linux-gnu --build x86_64-linux-gnu --target "$TARGET" --enable-unregisterised
|
||||||
|
RUN cp mk/flavours/quick-cross.mk mk/build.mk && make -j "$(nproc)"
|
||||||
|
RUN make install
|
||||||
|
RUN curl -L "https://downloads.haskell.org/~cabal/cabal-install-3.2.0.0/cabal-install-3.2.0.0-x86_64-unknown-linux.tar.xz" | tar xJv -C /usr/local/bin
|
||||||
|
|
||||||
|
# Due to an apparent cabal bug, we specify our options directly to cabal
|
||||||
|
# It won't reuse caches if ghc-options are specified in ~/.cabal/config
|
||||||
|
ENV CABALOPTS "--ghc-options;-optl-Wl,-fuse-ld=bfd -split-sections -optc-Os -optc-Wl,--gc-sections;--with-ghc=$TARGET-ghc;--with-hc-pkg=$TARGET-ghc-pkg"
|
||||||
|
|
||||||
|
# Prebuild the dependencies
|
||||||
|
#RUN cabal update && IFS=';' && cabal install $CABALOPTS --lib Diff-0.4.0 base-compat-0.11.2 base-orphans-0.8.4 dlist-1.0 hashable-1.3.0.0 indexed-traversable-0.1.1 integer-logarithms-1.0.3.1 primitive-0.7.1.0 regex-base-0.94.0.0 splitmix-0.1.0.3 tagged-0.8.6.1 th-abstraction-0.4.2.0 transformers-compat-0.6.6 base-compat-batteries-0.11.2 time-compat-1.9.5 unordered-containers-0.2.13.0 data-fix-0.3.1 vector-0.12.2.0 scientific-0.3.6.2 regex-tdfa-1.3.1.0 random-1.2.0 distributive-0.6.2.1 attoparsec-0.13.2.5 uuid-types-1.0.3 comonad-5.0.8 bifunctors-5.5.10 assoc-1.0.2 these-1.1.1.1 strict-0.4.0.1 aeson-1.5.5.1
|
||||||
|
|
||||||
|
# Copy the build script
|
||||||
|
COPY build /usr/bin
|
||||||
|
|
||||||
|
WORKDIR /scratch
|
||||||
|
ENTRYPOINT ["/usr/bin/build"]
|
15
build/linux.ppc64le/build
Executable file
15
build/linux.ppc64le/build
Executable file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -xe
|
||||||
|
{
|
||||||
|
tar xzv --strip-components=1
|
||||||
|
./striptests
|
||||||
|
mkdir "$TARGETNAME"
|
||||||
|
cabal update
|
||||||
|
( IFS=';'; cabal build $CABALOPTS --enable-executable-static )
|
||||||
|
find . -name shellcheck -type f -exec mv {} "$TARGETNAME/" \;
|
||||||
|
ls -l "$TARGETNAME"
|
||||||
|
"$TARGET-strip" -s "$TARGETNAME/shellcheck"
|
||||||
|
ls -l "$TARGETNAME"
|
||||||
|
qemu-ppc64le-static "$TARGETNAME/shellcheck" --version
|
||||||
|
} >&2
|
||||||
|
tar czv "$TARGETNAME"
|
1
build/linux.ppc64le/tag
Normal file
1
build/linux.ppc64le/tag
Normal file
@@ -0,0 +1 @@
|
|||||||
|
koalaman/scbuilder-linux-ppc64le
|
@@ -2,7 +2,7 @@
|
|||||||
set -xe
|
set -xe
|
||||||
{
|
{
|
||||||
tar xzv --strip-components=1
|
tar xzv --strip-components=1
|
||||||
chmod +x striptests && ./striptests
|
./striptests
|
||||||
mkdir "$TARGETNAME"
|
mkdir "$TARGETNAME"
|
||||||
cabal update
|
cabal update
|
||||||
( IFS=';'; cabal build $CABALOPTS --enable-executable-static )
|
( IFS=';'; cabal build $CABALOPTS --enable-executable-static )
|
||||||
|
@@ -5,7 +5,7 @@ ENV TARGETNAME windows.x86_64
|
|||||||
# We don't need wine32, even though it complains
|
# We don't need wine32, even though it complains
|
||||||
USER root
|
USER root
|
||||||
ENV DEBIAN_FRONTEND noninteractive
|
ENV DEBIAN_FRONTEND noninteractive
|
||||||
RUN apt-get update && apt-get install -y curl busybox wine winbind
|
RUN apt-get update && apt-get install -y curl busybox wine
|
||||||
|
|
||||||
# Fetch Windows version, will be available under z:\haskell
|
# Fetch Windows version, will be available under z:\haskell
|
||||||
WORKDIR /haskell
|
WORKDIR /haskell
|
||||||
@@ -19,8 +19,8 @@ ENV WINEPATH /haskell/bin
|
|||||||
# that necessitated this but I don't care enough to find out
|
# that necessitated this but I don't care enough to find out
|
||||||
ENV CABALOPTS "--ghc-options;-split-sections -optc-Os -optc-Wl,--gc-sections"
|
ENV CABALOPTS "--ghc-options;-split-sections -optc-Os -optc-Wl,--gc-sections"
|
||||||
|
|
||||||
# Precompile some deps to speed up later builds
|
# Precompile some deps to speed up later builds. This list is just copied from `cabal build`
|
||||||
RUN wine /haskell/bin/cabal.exe update && IFS=';' && wine /haskell/bin/cabal.exe install --lib --dependencies-only $CABALOPTS ShellCheck
|
RUN wine /haskell/bin/cabal.exe update && IFS=';' && wine /haskell/bin/cabal.exe install $CABALOPTS --lib Diff-0.4.0 base-compat-0.11.2 base-orphans-0.8.4 dlist-1.0 hashable-1.3.0.0 indexed-traversable-0.1.1 integer-logarithms-1.0.3.1 primitive-0.7.1.0 regex-base-0.94.0.0 splitmix-0.1.0.3 tagged-0.8.6.1 th-abstraction-0.4.2.0 transformers-compat-0.6.6 base-compat-batteries-0.11.2 time-compat-1.9.5 unordered-containers-0.2.13.0 data-fix-0.3.1 vector-0.12.2.0 scientific-0.3.6.2 regex-tdfa-1.3.1.0 random-1.2.0 distributive-0.6.2.1 attoparsec-0.13.2.5 uuid-types-1.0.3 comonad-5.0.8 bifunctors-5.5.10 assoc-1.0.2 these-1.1.1.1 strict-0.4.0.1 aeson-1.5.5.1
|
||||||
|
|
||||||
COPY build /usr/bin
|
COPY build /usr/bin
|
||||||
WORKDIR /scratch
|
WORKDIR /scratch
|
||||||
|
@@ -6,7 +6,7 @@ cabal() {
|
|||||||
set -xe
|
set -xe
|
||||||
{
|
{
|
||||||
tar xzv --strip-components=1
|
tar xzv --strip-components=1
|
||||||
chmod +x striptests && ./striptests
|
./striptests
|
||||||
mkdir "$TARGETNAME"
|
mkdir "$TARGETNAME"
|
||||||
cabal update
|
cabal update
|
||||||
( IFS=';'; cabal build $CABALOPTS )
|
( IFS=';'; cabal build $CABALOPTS )
|
||||||
|
@@ -17,11 +17,9 @@
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
-}
|
-}
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
|
||||||
module ShellCheck.ASTLib where
|
module ShellCheck.ASTLib where
|
||||||
|
|
||||||
import ShellCheck.AST
|
import ShellCheck.AST
|
||||||
import ShellCheck.Regex
|
|
||||||
|
|
||||||
import Control.Monad.Writer
|
import Control.Monad.Writer
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
@@ -33,8 +31,6 @@ import Data.Maybe
|
|||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
import Numeric (showHex)
|
import Numeric (showHex)
|
||||||
|
|
||||||
import Test.QuickCheck
|
|
||||||
|
|
||||||
arguments (T_SimpleCommand _ _ (cmd:args)) = args
|
arguments (T_SimpleCommand _ _ (cmd:args)) = args
|
||||||
|
|
||||||
-- Is this a type of loop?
|
-- Is this a type of loop?
|
||||||
@@ -676,43 +672,3 @@ isAnnotationIgnoringCode code t =
|
|||||||
where
|
where
|
||||||
hasNum (DisableComment from to) = code >= from && code < to
|
hasNum (DisableComment from to) = code >= from && code < to
|
||||||
hasNum _ = False
|
hasNum _ = False
|
||||||
|
|
||||||
prop_executableFromShebang1 = executableFromShebang "/bin/sh" == "sh"
|
|
||||||
prop_executableFromShebang2 = executableFromShebang "/bin/bash" == "bash"
|
|
||||||
prop_executableFromShebang3 = executableFromShebang "/usr/bin/env ksh" == "ksh"
|
|
||||||
prop_executableFromShebang4 = executableFromShebang "/usr/bin/env -S foo=bar bash -x" == "bash"
|
|
||||||
prop_executableFromShebang5 = executableFromShebang "/usr/bin/env --split-string=bash -x" == "bash"
|
|
||||||
prop_executableFromShebang6 = executableFromShebang "/usr/bin/env --split-string=foo=bar bash -x" == "bash"
|
|
||||||
prop_executableFromShebang7 = executableFromShebang "/usr/bin/env --split-string bash -x" == "bash"
|
|
||||||
prop_executableFromShebang8 = executableFromShebang "/usr/bin/env --split-string foo=bar bash -x" == "bash"
|
|
||||||
prop_executableFromShebang9 = executableFromShebang "/usr/bin/env foo=bar dash" == "dash"
|
|
||||||
prop_executableFromShebang10 = executableFromShebang "/bin/busybox sh" == "ash"
|
|
||||||
prop_executableFromShebang11 = executableFromShebang "/bin/busybox ash" == "ash"
|
|
||||||
|
|
||||||
-- Get the shell executable from a string like '/usr/bin/env bash'
|
|
||||||
executableFromShebang :: String -> String
|
|
||||||
executableFromShebang = shellFor
|
|
||||||
where
|
|
||||||
re = mkRegex "/env +(-S|--split-string=?)? *(.*)"
|
|
||||||
shellFor s | s `matches` re =
|
|
||||||
case matchRegex re s of
|
|
||||||
Just [flag, shell] -> fromEnvArgs (words shell)
|
|
||||||
_ -> ""
|
|
||||||
shellFor sb =
|
|
||||||
case words sb of
|
|
||||||
[] -> ""
|
|
||||||
[x] -> basename x
|
|
||||||
(first:second:args) | basename first == "busybox" ->
|
|
||||||
case basename second of
|
|
||||||
"sh" -> "ash" -- busybox sh is ash
|
|
||||||
x -> x
|
|
||||||
(first:args) | basename first == "env" ->
|
|
||||||
fromEnvArgs args
|
|
||||||
(first:_) -> basename first
|
|
||||||
|
|
||||||
fromEnvArgs args = fromMaybe "" $ find (notElem '=') $ skipFlags args
|
|
||||||
basename s = reverse . takeWhile (/= '/') . reverse $ s
|
|
||||||
skipFlags = dropWhile ("-" `isPrefixOf`)
|
|
||||||
|
|
||||||
return []
|
|
||||||
runTests = $quickCheckAll
|
|
||||||
|
@@ -196,7 +196,6 @@ nodeChecks = [
|
|||||||
,checkAssignToSelf
|
,checkAssignToSelf
|
||||||
,checkEqualsInCommand
|
,checkEqualsInCommand
|
||||||
,checkSecondArgIsComparison
|
,checkSecondArgIsComparison
|
||||||
,checkComparisonWithLeadingX
|
|
||||||
]
|
]
|
||||||
|
|
||||||
optionalChecks = map fst optionalTreeChecks
|
optionalChecks = map fst optionalTreeChecks
|
||||||
@@ -244,6 +243,13 @@ optionalTreeChecks = [
|
|||||||
cdPositive = "echo $VAR",
|
cdPositive = "echo $VAR",
|
||||||
cdNegative = "VAR=hello; echo $VAR"
|
cdNegative = "VAR=hello; echo $VAR"
|
||||||
}, checkUnassignedReferences' True)
|
}, checkUnassignedReferences' True)
|
||||||
|
|
||||||
|
,(newCheckDescription {
|
||||||
|
cdName = "avoid-x-comparisons",
|
||||||
|
cdDescription = "Warn about 'x'-prefix in comparisons",
|
||||||
|
cdPositive = "[ \"x$var\" = xval ]",
|
||||||
|
cdNegative = "[ \"$var\" = val ]"
|
||||||
|
}, nodeChecksToTreeCheck [checkComparisonWithLeadingX])
|
||||||
]
|
]
|
||||||
|
|
||||||
optionalCheckMap :: Map.Map String (Parameters -> Token -> [TokenComment])
|
optionalCheckMap :: Map.Map String (Parameters -> Token -> [TokenComment])
|
||||||
@@ -584,12 +590,6 @@ prop_checkShebang9 = verifyNotTree checkShebang "# shellcheck shell=sh\ntrue"
|
|||||||
prop_checkShebang10= verifyNotTree checkShebang "#!foo\n# shellcheck shell=sh ignore=SC2239\ntrue"
|
prop_checkShebang10= verifyNotTree checkShebang "#!foo\n# shellcheck shell=sh ignore=SC2239\ntrue"
|
||||||
prop_checkShebang11= verifyTree checkShebang "#!/bin/sh/\ntrue"
|
prop_checkShebang11= verifyTree checkShebang "#!/bin/sh/\ntrue"
|
||||||
prop_checkShebang12= verifyTree checkShebang "#!/bin/sh/ -xe\ntrue"
|
prop_checkShebang12= verifyTree checkShebang "#!/bin/sh/ -xe\ntrue"
|
||||||
prop_checkShebang13= verifyTree checkShebang "#!/bin/busybox sh"
|
|
||||||
prop_checkShebang14= verifyNotTree checkShebang "#!/bin/busybox sh\n# shellcheck shell=sh\n"
|
|
||||||
prop_checkShebang15= verifyNotTree checkShebang "#!/bin/busybox sh\n# shellcheck shell=dash\n"
|
|
||||||
prop_checkShebang16= verifyTree checkShebang "#!/bin/busybox ash"
|
|
||||||
prop_checkShebang17= verifyNotTree checkShebang "#!/bin/busybox ash\n# shellcheck shell=dash\n"
|
|
||||||
prop_checkShebang18= verifyNotTree checkShebang "#!/bin/busybox ash\n# shellcheck shell=sh\n"
|
|
||||||
checkShebang params (T_Annotation _ list t) =
|
checkShebang params (T_Annotation _ list t) =
|
||||||
if any isOverride list then [] else checkShebang params t
|
if any isOverride list then [] else checkShebang params t
|
||||||
where
|
where
|
||||||
@@ -1015,7 +1015,6 @@ checkSingleQuotedVariables params t@(T_SingleQuoted id s) =
|
|||||||
,"alias"
|
,"alias"
|
||||||
,"sudo" -- covering "sudo sh" and such
|
,"sudo" -- covering "sudo sh" and such
|
||||||
,"docker" -- like above
|
,"docker" -- like above
|
||||||
,"podman"
|
|
||||||
,"dpkg-query"
|
,"dpkg-query"
|
||||||
,"jq" -- could also check that user provides --arg
|
,"jq" -- could also check that user provides --arg
|
||||||
,"rename"
|
,"rename"
|
||||||
@@ -1217,8 +1216,8 @@ checkQuotedCondRegex _ (TC_Binary _ _ "=~" _ rhs) =
|
|||||||
where
|
where
|
||||||
error t =
|
error t =
|
||||||
unless (isConstantNonRe t) $
|
unless (isConstantNonRe t) $
|
||||||
warn (getId t) 2076
|
err (getId t) 2076
|
||||||
"Remove quotes from right-hand side of =~ to match as a regex rather than literally."
|
"Don't quote right-hand side of =~, it'll match literally rather than as a regex."
|
||||||
re = mkRegex "[][*.+()|]"
|
re = mkRegex "[][*.+()|]"
|
||||||
hasMetachars s = s `matches` re
|
hasMetachars s = s `matches` re
|
||||||
isConstantNonRe t = fromMaybe False $ do
|
isConstantNonRe t = fromMaybe False $ do
|
||||||
@@ -4000,7 +3999,7 @@ checkComparisonWithLeadingX params t =
|
|||||||
check lhs rhs
|
check lhs rhs
|
||||||
_ -> return ()
|
_ -> return ()
|
||||||
where
|
where
|
||||||
msg = "Avoid x-prefix in comparisons as it no longer serves a purpose."
|
msg = "Avoid outdated x-prefix in comparisons as it no longer serves a purpose."
|
||||||
check lhs rhs = sequence_ $ do
|
check lhs rhs = sequence_ $ do
|
||||||
l <- fixLeadingX lhs
|
l <- fixLeadingX lhs
|
||||||
r <- fixLeadingX rhs
|
r <- fixLeadingX rhs
|
||||||
|
@@ -240,8 +240,6 @@ prop_determineShell7 = determineShellTest "#! /bin/ash" == Dash
|
|||||||
prop_determineShell8 = determineShellTest' (Just Ksh) "#!/bin/sh" == Sh
|
prop_determineShell8 = determineShellTest' (Just Ksh) "#!/bin/sh" == Sh
|
||||||
prop_determineShell9 = determineShellTest "#!/bin/env -S dash -x" == Dash
|
prop_determineShell9 = determineShellTest "#!/bin/env -S dash -x" == Dash
|
||||||
prop_determineShell10 = determineShellTest "#!/bin/env --split-string= dash -x" == Dash
|
prop_determineShell10 = determineShellTest "#!/bin/env --split-string= dash -x" == Dash
|
||||||
prop_determineShell11 = determineShellTest "#!/bin/busybox sh" == Dash -- busybox sh is a specific shell, not posix sh
|
|
||||||
prop_determineShell12 = determineShellTest "#!/bin/busybox ash" == Dash
|
|
||||||
|
|
||||||
determineShellTest = determineShellTest' Nothing
|
determineShellTest = determineShellTest' Nothing
|
||||||
determineShellTest' fallbackShell = determineShell fallbackShell . fromJust . prRoot . pScript
|
determineShellTest' fallbackShell = determineShell fallbackShell . fromJust . prRoot . pScript
|
||||||
@@ -255,6 +253,19 @@ determineShell fallbackShell t = fromMaybe Bash $
|
|||||||
headOrDefault (fromShebang s) [s | ShellOverride s <- annotations]
|
headOrDefault (fromShebang s) [s | ShellOverride s <- annotations]
|
||||||
fromShebang (T_Script _ (T_Literal _ s) _) = executableFromShebang s
|
fromShebang (T_Script _ (T_Literal _ s) _) = executableFromShebang s
|
||||||
|
|
||||||
|
-- Given a string like "/bin/bash" or "/usr/bin/env dash",
|
||||||
|
-- return the shell basename like "bash" or "dash"
|
||||||
|
executableFromShebang :: String -> String
|
||||||
|
executableFromShebang = shellFor
|
||||||
|
where
|
||||||
|
shellFor s | "/env " `isInfixOf` s = case matchRegex re s of
|
||||||
|
Just [flag, shell] -> shell
|
||||||
|
_ -> ""
|
||||||
|
shellFor s | ' ' `elem` s = shellFor $ takeWhile (/= ' ') s
|
||||||
|
shellFor s = reverse . takeWhile (/= '/') . reverse $ s
|
||||||
|
re = mkRegex "/env +(-S|--split-string=?)? *([^ ]*)"
|
||||||
|
|
||||||
|
|
||||||
-- Given a root node, make a map from Id to parent Token.
|
-- Given a root node, make a map from Id to parent Token.
|
||||||
-- This is used to populate parentMap in Parameters
|
-- This is used to populate parentMap in Parameters
|
||||||
getParentTree :: Token -> Map.Map Id Token
|
getParentTree :: Token -> Map.Map Id Token
|
||||||
|
@@ -1056,7 +1056,7 @@ checkFindRedirections = CommandCheck (Basename "find") f
|
|||||||
|
|
||||||
prop_checkWhich = verify checkWhich "which '.+'"
|
prop_checkWhich = verify checkWhich "which '.+'"
|
||||||
checkWhich = CommandCheck (Basename "which") $
|
checkWhich = CommandCheck (Basename "which") $
|
||||||
\t -> info (getId $ getCommandTokenOrThis t) 2230 "'which' is non-standard. Use builtin 'command -v' instead."
|
\t -> info (getId $ getCommandTokenOrThis t) 2230 "which is non-standard. Use builtin 'command -v' instead."
|
||||||
|
|
||||||
prop_checkSudoRedirect1 = verify checkSudoRedirect "sudo echo 3 > /proc/file"
|
prop_checkSudoRedirect1 = verify checkSudoRedirect "sudo echo 3 > /proc/file"
|
||||||
prop_checkSudoRedirect2 = verify checkSudoRedirect "sudo cmd < input"
|
prop_checkSudoRedirect2 = verify checkSudoRedirect "sudo cmd < input"
|
||||||
|
@@ -73,15 +73,15 @@ import qualified Data.Map as Map
|
|||||||
|
|
||||||
|
|
||||||
data SystemInterface m = SystemInterface {
|
data SystemInterface m = SystemInterface {
|
||||||
-- | Read a file by filename, or return an error
|
-- Read a file by filename, or return an error
|
||||||
siReadFile :: String -> m (Either ErrorMessage String),
|
siReadFile :: String -> m (Either ErrorMessage String),
|
||||||
-- | Given:
|
-- Given:
|
||||||
-- the current script,
|
-- the current script,
|
||||||
-- a list of source-path annotations in effect,
|
-- a list of source-path annotations in effect,
|
||||||
-- and a sourced file,
|
-- and a sourced file,
|
||||||
-- find the sourced file
|
-- find the sourced file
|
||||||
siFindSource :: String -> [String] -> String -> m FilePath,
|
siFindSource :: String -> [String] -> String -> m FilePath,
|
||||||
-- | Get the configuration file (name, contents) for a filename
|
-- Get the configuration file (name, contents) for a filename
|
||||||
siGetConfig :: String -> m (Maybe (FilePath, String))
|
siGetConfig :: String -> m (Maybe (FilePath, String))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,7 +24,7 @@
|
|||||||
module ShellCheck.Parser (parseScript, runTests) where
|
module ShellCheck.Parser (parseScript, runTests) where
|
||||||
|
|
||||||
import ShellCheck.AST
|
import ShellCheck.AST
|
||||||
import ShellCheck.ASTLib hiding (runTests)
|
import ShellCheck.ASTLib
|
||||||
import ShellCheck.Data
|
import ShellCheck.Data
|
||||||
import ShellCheck.Interface
|
import ShellCheck.Interface
|
||||||
|
|
||||||
@@ -87,23 +87,11 @@ extglobStart = oneOf extglobStartChars
|
|||||||
unicodeDoubleQuotes = "\x201C\x201D\x2033\x2036"
|
unicodeDoubleQuotes = "\x201C\x201D\x2033\x2036"
|
||||||
unicodeSingleQuotes = "\x2018\x2019"
|
unicodeSingleQuotes = "\x2018\x2019"
|
||||||
|
|
||||||
prop_spacing1 = isOk spacing " \\\n # Comment"
|
prop_spacing = isOk spacing " \\\n # Comment"
|
||||||
prop_spacing2 = isOk spacing "# We can continue lines with \\"
|
|
||||||
prop_spacing3 = isWarning spacing " \\\n # --verbose=true \\"
|
|
||||||
spacing = do
|
spacing = do
|
||||||
x <- many (many1 linewhitespace <|> continuation)
|
x <- many (many1 linewhitespace <|> try (string "\\\n" >> return ""))
|
||||||
optional readComment
|
optional readComment
|
||||||
return $ concat x
|
return $ concat x
|
||||||
where
|
|
||||||
continuation = do
|
|
||||||
try (string "\\\n")
|
|
||||||
-- The line was continued. Warn if this next line is a comment with a trailing \
|
|
||||||
whitespace <- many linewhitespace
|
|
||||||
optional $ do
|
|
||||||
x <- readComment
|
|
||||||
when ("\\" `isSuffixOf` x) $
|
|
||||||
parseProblem ErrorC 1143 "This backslash is part of a comment and does not continue the line."
|
|
||||||
return whitespace
|
|
||||||
|
|
||||||
spacing1 = do
|
spacing1 = do
|
||||||
spacing <- spacing
|
spacing <- spacing
|
||||||
@@ -1051,7 +1039,6 @@ readComment = do
|
|||||||
unexpecting "shellcheck annotation" readAnnotationPrefix
|
unexpecting "shellcheck annotation" readAnnotationPrefix
|
||||||
readAnyComment
|
readAnyComment
|
||||||
|
|
||||||
prop_readAnyComment = isOk readAnyComment "# Comment"
|
|
||||||
readAnyComment = do
|
readAnyComment = do
|
||||||
char '#'
|
char '#'
|
||||||
many $ noneOf "\r\n"
|
many $ noneOf "\r\n"
|
||||||
@@ -1417,8 +1404,6 @@ readNormalEscaped = called "escaped char" $ do
|
|||||||
do
|
do
|
||||||
next <- quotable <|> oneOf "?*@!+[]{}.,~#"
|
next <- quotable <|> oneOf "?*@!+[]{}.,~#"
|
||||||
when (next == ' ') $ checkTrailingSpaces pos <|> return ()
|
when (next == ' ') $ checkTrailingSpaces pos <|> return ()
|
||||||
-- Check if this line is followed by a commented line with a trailing backslash
|
|
||||||
when (next == '\n') $ try . lookAhead $ void spacing
|
|
||||||
return $ if next == '\n' then "" else [next]
|
return $ if next == '\n' then "" else [next]
|
||||||
<|>
|
<|>
|
||||||
do
|
do
|
||||||
@@ -3231,8 +3216,8 @@ readScriptFile sourced = do
|
|||||||
let ignoreShebang = shellAnnotationSpecified || shellFlagSpecified
|
let ignoreShebang = shellAnnotationSpecified || shellFlagSpecified
|
||||||
|
|
||||||
unless ignoreShebang $
|
unless ignoreShebang $
|
||||||
verifyShebang pos (executableFromShebang shebangString)
|
verifyShebang pos (getShell shebangString)
|
||||||
if ignoreShebang || isValidShell (executableFromShebang shebangString) /= Just False
|
if ignoreShebang || isValidShell (getShell shebangString) /= Just False
|
||||||
then do
|
then do
|
||||||
commands <- withAnnotations annotations readCompoundListOrEmpty
|
commands <- withAnnotations annotations readCompoundListOrEmpty
|
||||||
id <- endSpan start
|
id <- endSpan start
|
||||||
@@ -3246,6 +3231,17 @@ readScriptFile sourced = do
|
|||||||
return $ T_Script id shebang []
|
return $ T_Script id shebang []
|
||||||
|
|
||||||
where
|
where
|
||||||
|
basename s = reverse . takeWhile (/= '/') . reverse $ s
|
||||||
|
skipFlags = dropWhile ("-" `isPrefixOf`)
|
||||||
|
getShell sb =
|
||||||
|
case words sb of
|
||||||
|
[] -> ""
|
||||||
|
[x] -> basename x
|
||||||
|
(first:args) ->
|
||||||
|
if basename first == "env"
|
||||||
|
then fromMaybe "" $ find (notElem '=') $ skipFlags args
|
||||||
|
else basename first
|
||||||
|
|
||||||
verifyShebang pos s = do
|
verifyShebang pos s = do
|
||||||
case isValidShell s of
|
case isValidShell s of
|
||||||
Just True -> return ()
|
Just True -> return ()
|
||||||
|
@@ -4,7 +4,6 @@ import Control.Monad
|
|||||||
import System.Exit
|
import System.Exit
|
||||||
import qualified ShellCheck.Analytics
|
import qualified ShellCheck.Analytics
|
||||||
import qualified ShellCheck.AnalyzerLib
|
import qualified ShellCheck.AnalyzerLib
|
||||||
import qualified ShellCheck.ASTLib
|
|
||||||
import qualified ShellCheck.Checker
|
import qualified ShellCheck.Checker
|
||||||
import qualified ShellCheck.Checks.Commands
|
import qualified ShellCheck.Checks.Commands
|
||||||
import qualified ShellCheck.Checks.Custom
|
import qualified ShellCheck.Checks.Custom
|
||||||
@@ -18,7 +17,6 @@ main = do
|
|||||||
results <- sequence [
|
results <- sequence [
|
||||||
ShellCheck.Analytics.runTests
|
ShellCheck.Analytics.runTests
|
||||||
,ShellCheck.AnalyzerLib.runTests
|
,ShellCheck.AnalyzerLib.runTests
|
||||||
,ShellCheck.ASTLib.runTests
|
|
||||||
,ShellCheck.Checker.runTests
|
,ShellCheck.Checker.runTests
|
||||||
,ShellCheck.Checks.Commands.runTests
|
,ShellCheck.Checks.Commands.runTests
|
||||||
,ShellCheck.Checks.Custom.runTests
|
,ShellCheck.Checks.Custom.runTests
|
||||||
|
Reference in New Issue
Block a user