ShellCheck est un outil GPLv3 qui donne des avertissements et des suggestions pour les scripts shell bash/sh :
Les objectifs de ShellCheck sont
Pour signaler et clarifier les problèmes de syntaxe typiques du débutant qui amènent un shell à donner des messages d'erreur énigmatiques.
Souligner et clarifier les problèmes sémantiques typiques de niveau intermédiaire qui provoquent un comportement étrange et contre-intuitif d'un shell.
Pour souligner les mises en garde subtiles, les cas extrêmes et les pièges qui peuvent entraîner l'échec du script d'un utilisateur avancé, par ailleurs fonctionnel, dans des circonstances futures.
Consultez la galerie de mauvais codes pour des exemples de ce que ShellCheck peut vous aider à identifier !
Il existe plusieurs façons d'utiliser ShellCheck !
Collez un script shell sur https://www.shellcheck.net pour un retour instantané.
ShellCheck.net est toujours synchronisé avec le dernier commit git et constitue le moyen le plus simple d'essayer ShellCheck. Parlez-en à vos amis !
Exécutez shellcheck yourscript
dans votre terminal pour une sortie instantanée, comme vu ci-dessus.
Vous pouvez voir les suggestions ShellCheck directement dans divers éditeurs.
.
.
Sublime, via SublimeLinter.
Pulsar Edit (ancien Atom), via linter-shellcheck-pulsar.
VSCode, via vscode-shellcheck.
La plupart des autres éditeurs, grâce à la compatibilité des erreurs GCC.
Bien que ShellCheck soit principalement destiné à une utilisation interactive, il peut facilement être ajouté aux builds ou aux suites de tests. Il utilise canoniquement les codes de sortie, vous pouvez donc simplement ajouter une commande shellcheck
dans le cadre du processus.
Par exemple, dans un Makefile :
check-scripts :
# Fail if any of these files have warnings
shellcheck myscripts/*.sh
ou dans un fichier Travis CI .travis.yml
:
script :
# Fail if any of these files have warnings
- shellcheck myscripts/*.sh
Services et plates-formes sur lesquels ShellCheck est préinstallé et prêt à l'emploi :
La plupart des autres services, y compris GitLab, vous permettent d'installer ShellCheck vous-même, soit via le gestionnaire de packages du système (voir Installation), soit en téléchargeant et en décompressant une version binaire.
C'est quand même une bonne idée d'installer manuellement une version spécifique de ShellCheck. Cela évite toute rupture surprise de build lorsqu’une nouvelle version avec de nouveaux avertissements est publiée.
Pour un filtrage ou des rapports personnalisés, ShellCheck peut générer des avertissements simples JSON, XML compatible CheckStyle, GCC ainsi qu'un texte lisible par l'homme (avec ou sans couleurs ANSI). Consultez la page wiki d'intégration pour plus de documentation.
Le moyen le plus simple d’installer ShellCheck localement consiste à utiliser votre gestionnaire de packages.
Sur les systèmes avec Cabal (s'installe sur ~/.cabal/bin
) :
cabal update
cabal install ShellCheck
Sur les systèmes avec Stack (installe sur ~/.local/bin
) :
stack update
stack install ShellCheck
Sur les distributions basées sur Debian :
sudo apt install shellcheck
Sur les distributions basées sur Arch Linux :
pacman -S shellcheck
ou obtenez le shellcheck-bin sans dépendance auprès de l'AUR.
Sur les distributions basées sur Gentoo :
emerge --ask shellcheck
Sur les distributions basées sur EPEL :
sudo yum -y install epel-release
sudo yum install ShellCheck
Sur les distributions basées sur Fedora :
dnf install ShellCheck
Sur FreeBSD :
pkg install hs-ShellCheck
Sur macOS (OS X) avec Homebrew :
brew install shellcheck
Ou avec MacPorts :
sudo port install shellcheck
Sur OpenBSD :
pkg_add shellcheck
Sur openSUSE
zypper in ShellCheck
Ou utilisez OneClickInstall - https://software.opensuse.org/package/ShellCheck
Sur Solus :
eopkg install shellcheck
Sous Windows (via chocolatey) :
C: > choco install shellcheck
Ou Windows (via Winget) :
C: > winget install --id koalaman.shellcheck
Ou Windows (via scoop) :
C: > scoop install shellcheck
De conda-forge :
conda install -c conda-forge shellcheck
Depuis Snap Store :
snap install --channel=edge shellcheck
Depuis Docker Hub :
docker run --rm -v " $PWD :/mnt " koalaman/shellcheck:stable myscript
# Or :v0.4.7 for that version, or :latest for daily builds
ou utilisez koalaman/shellcheck-alpine
si vous souhaitez étendre une image plus grande basée sur Alpine Linux. Cela fonctionne exactement comme une image Alpine classique, mais Shellcheck est préinstallé.
Utilisation du gestionnaire de packages nix :
nix-env -iA nixpkgs.shellcheck
Utiliser le gestionnaire de paquets Flox
flox install shellcheck
Vous pouvez également télécharger les binaires précompilés pour la dernière version ici :
ou consultez les versions de GitHub pour d'autres versions (y compris la dernière méta-version pour les versions quotidiennes de git).
Il n'existe actuellement aucun binaire officiel pour Apple Silicon, mais des versions tierces sont disponibles via ShellCheck pour Visual Studio Code.
Les packages de distribution sont déjà livrés avec une page man
. Si vous construisez à partir des sources, il peut être installé avec :
pandoc -s -f markdown-smart -t man shellcheck.1.md -o shellcheck.1
sudo mv shellcheck.1 /usr/share/man/man1
Pour exécuter ShellCheck via pré-commit, ajoutez le hook à votre .pre-commit-config.yaml
:
repos:
- repo: https://github.com/koalaman/shellcheck-precommit
rev: v0.7.2
hooks:
- id: shellcheck
# args: ["--severity=warning"] # Optionally only show errors and warnings
Travis CI a désormais intégré ShellCheck par défaut, vous n'avez donc pas besoin de l'installer manuellement.
Si vous souhaitez quand même procéder à la mise à niveau à votre guise ou vous assurer que vous utilisez la dernière version, suivez les étapes ci-dessous pour installer une version binaire.
Les binaires précompilés sont présentés dans des fichiers tar.xz
Pour les décompresser, assurez-vous que xz
est installé. Sur Debian/Ubuntu/Mint, vous pouvez apt install xz-utils
. Sur Redhat/Fedora/CentOS, yum -y install xz
.
Un simple installateur peut faire quelque chose comme :
scversion= " stable " # or "v0.4.7", or "latest"
wget -qO- " https://github.com/koalaman/shellcheck/releases/download/ ${scversion?} /shellcheck- ${scversion?} .linux.x86_64.tar.xz " | tar -xJv
cp " shellcheck- ${scversion} /shellcheck " /usr/bin/
shellcheck --version
Cette section décrit comment créer ShellCheck à partir d'un répertoire source. ShellCheck est écrit en Haskell et nécessite 2 Go de RAM pour être compilé.
ShellCheck est construit et empaqueté à l'aide de Cabal. Installez le paquet cabal-install
depuis le gestionnaire de paquets de votre système (avec par exemple apt-get
, brew
, emerge
, yum
ou zypper
).
Sur macOS (OS X), vous pouvez effectuer une installation rapide de Cabal en utilisant Brew, ce qui prend quelques minutes au lieu de plus de 30 minutes si vous essayez de le compiler à partir des sources.
$ brew install cabal-install
Sur MacPorts, le package s'appelle plutôt hs-cabal-install
, tandis que les utilisateurs natifs de Windows doivent installer la dernière version de la plate-forme Haskell à partir de https://www.haskell.org/platform/
Vérifiez que cabal
est installé et mettez à jour sa liste de dépendances avec
$ cabal update
git clone
ce référentiel et cd
dans le répertoire source ShellCheck pour construire/installer :
$ cabal install
Cela compilera ShellCheck et l'installera dans votre répertoire ~/.cabal/bin
.
Ajoutez ce répertoire à votre PATH
(pour bash, ajoutez ceci à votre ~/.bashrc
) :
export PATH= " $HOME /.cabal/bin: $PATH "
Déconnectez-vous puis reconnectez-vous et vérifiez que votre PATH est correctement configuré :
$ which shellcheck
~ /.cabal/bin/shellcheck
Sous Windows natif, le PATH
doit déjà être configuré, mais le système peut utiliser une page de codes héritée. Dans cmd.exe
, powershell.exe
et Powershell ISE, assurez-vous d'utiliser une police TrueType, pas une police Raster, et définissez la page de codes active sur UTF-8 (65001) avec chcp
:
chcp 65001
Dans Powershell ISE, vous devrez peut-être mettre à jour en plus le codage de sortie :
[ Console ]::OutputEncoding = [ System.Text.Encoding ]::UTF8
Pour exécuter la suite de tests unitaires :
$ cabal test
Alors, quel genre de choses ShellCheck recherche-t-il ? Voici une liste incomplète des problèmes détectés.
ShellCheck peut reconnaître plusieurs types de citations incorrectes :
echo $1 # Unquoted variables
find . -name * .ogg # Unquoted find/grep patterns
rm " ~/my file.txt " # Quoted tilde expansion
v= ' --verbose="true" ' ; cmd $v # Literal quotes in variables
for f in " *.ogg " # Incorrectly quoted 'for' loops
touch $@ # Unquoted $@
echo ' Don ' t forget to restart ! ' # Singlequote closed by apostrophe
echo ' Don ' t try this at home ' # Attempting to escape ' in ' '
echo ' Path is $PATH ' # Variables in single quotes
trap " echo Took ${SECONDS} s " 0 # Prematurely expanded trap
unset var[i] # Array index treated as glob
ShellCheck peut reconnaître de nombreux types d'instructions de test incorrectes.
[[ n != 0 ]] # Constant test expressions
[[ -e * .mpg ]] # Existence checks of globs
[[ $foo == 0 ]] # Always true due to missing spaces
[[ -n " $foo " ]] # Always true due to literals
[[ $foo =~ " fo+ " ]] # Quoted regex in =~
[ foo =~ re ] # Unsupported [ ] operators
[ $1 -eq " shellcheck " ] # Numerical comparison of strings
[ $n && $m ] # && in [ .. ]
[ grep -q foo file ] # Command without $(..)
[[ " $$ file " == * .jpg ]] # Comparisons that can't succeed
(( 1 - lt 2 )) # Using test operators in ((..))
[ x ] & [ y ] | [ z ] # Accidental backgrounding and piping
ShellCheck peut reconnaître les cas où les commandes sont utilisées de manière incorrecte :
grep ' *foo* ' file # Globs in regex contexts
find . -exec foo {} && bar {} ; # Prematurely terminated find -exec
sudo echo ' Var=42 ' > /etc/profile # Redirecting sudo
time --format=%s sleep 10 # Passing time(1) flags to time builtin
while read h ; do ssh " $h " uptime # Commands eating while loop input
alias archive= ' mv $1 /backup ' # Defining aliases with arguments
tr -cd ' [a-zA-Z0-9] ' # [] around ranges in tr
exec foo ; echo " Done! " # Misused 'exec'
find -name * .bak -o -name * ~ -delete # Implicit precedence in find
# find . -exec foo > bar ; # Redirections in find
f () { whoami ; }; sudo f # External use of internal functions
ShellCheck reconnaît de nombreuses erreurs de syntaxe courantes pour les débutants :
var = 42 # Spaces around = in assignments
$foo =42 # $ in assignments
for $var in * ; do ... # $ in for loop variables
var $n = " Hello " # Wrong indirect assignment
echo ${var $n } # Wrong indirect reference
var=(1, 2, 3) # Comma separated arrays
array=( [index] = value ) # Incorrect index initialization
echo $var [14] # Missing {} in array references
echo " Argument 10 is $1 0 " # Positional parameter misreference
if $( myfunction ) ; then .. ; fi # Wrapping commands in $()
else if othercondition ; then .. # Using 'else if'
f ; f () { echo " hello world; } # Using function before definition
[ false ] # 'false' being true
if ( -f file ) # Using (..) instead of test
ShellCheck peut faire des suggestions pour améliorer le style :
[[ -z $( find /tmp | grep mpg ) ]] # Use grep -q instead
a >> log ; b >> log ; c >> log # Use a redirection block instead
echo " The time is ` date ` " # Use $() instead
cd dir ; process * ; cd .. ; # Use subshells instead
echo $[1+2] # Use standard $((..)) instead of old $[]
echo $(( $RANDOM % 6 )) # Don't use $ on variables in $((..))
echo " $( date ) " # Useless use of echo
cat file | grep foo # Useless use of cat
ShellCheck peut reconnaître les problèmes liés aux données et à la saisie :
args= " $@ " # Assigning arrays to strings
files=(foo bar) ; echo " $files " # Referencing arrays as strings
declare -A arr=(foo bar) # Associative arrays without index
printf " %sn " " Arguments: $@ . " # Concatenating strings and arrays
[[ $# > 2 ]] # Comparing numbers as strings
var=World ; echo " Hello " var # Unused lowercase variables
echo " Hello $name " # Unassigned lowercase variables
cmd | read bar ; echo $bar # Assignments in subshells
cat foo | cp bar # Piping to commands that don't read
printf ' %s: %sn ' foo # Mismatches in printf argument count
eval " ${array[@]} " # Lost word boundaries in array eval
for i in " ${x[@]} " ; do ${x[$i]} # Using array value as key
ShellCheck peut faire des suggestions pour améliorer la robustesse d'un script :
rm -rf " $STEAMROOT / " * # Catastrophic rm
touch ./-l ; ls * # Globs that could become options
find . -exec sh -c ' a && b {} ' ; # Find -exec shell injection
printf " Hello $name " # Variables in printf format
for f in $( ls * .txt ) ; do # Iterating over ls output
export MYVAR= $( cmd ) # Masked exit codes
case $version in 2. * ) : ;; 2.6. * ) # Shadowed case branches
ShellCheck vous avertira lors de l'utilisation de fonctionnalités non prises en charge par le shebang. Par exemple, si vous définissez le shebang sur #!/bin/sh
, ShellCheck vous avertira des problèmes de portabilité similaires aux checkbashisms
:
echo {1.. $n } # Works in ksh, but not bash/dash/sh
echo {1..10} # Works in ksh and bash, but not dash/sh
echo -n 42 # Works in ksh, bash and dash, undefined in sh
expr match str regex # Unportable alias for `expr str : regex`
trap ' exit 42 ' sigint # Unportable signal spec
cmd & > file # Unportable redirection operator
read foo < /dev/tcp/host/22 # Unportable intercepted files
foo-bar () { .. ; } # Undefined/unsupported function name
[ $UID = 0 ] # Variable undefined in dash/sh
local var=value # local is undefined in sh
time sleep 1 | sleep 5 # Undefined uses of 'time'
ShellCheck reconnaît une ménagerie d'autres problèmes :
PS1= ' e[0;32m$e[0m ' # PS1 colors not in [..]
PATH= " $PATH :~/bin " # Literal tilde in $PATH
rm “file” # Unicode quotes
echo " Hello world " # Carriage return / DOS line endings
echo hello # Trailing spaces after
var=42 echo $var # Expansion of inlined environment
! # bin/bash -x -e # Common shebang errors
echo $(( n / 180 * 100 )) # Unnecessary loss of precision
ls * [:digit:].txt # Bad character class globs
sed ' s/foo/bar/ ' file > file # Redirecting to input
var2= $var2 # Variable assigned to itself
[ x $var = xval ] # Antiquated x-comparisons
ls () { ls -l " $@ " ; } # Infinitely recursive wrapper
alias ls= ' ls -l ' ; ls foo # Alias used before it takes effect
for x ; do for x ; do # Nested loop uses same variable
while getopts " a " f ; do case $f in " b " ) # Unhandled getopts flags
Au début, vous dites "shellcheck est génial", mais ensuite vous vous dites "wtf, utilisons-nous toujours bash"
Alexandre Tarasikov, via Twitter
Les problèmes peuvent être ignorés via une variable d'environnement, une ligne de commande, individuellement ou globalement dans un fichier :
https://github.com/koalaman/shellcheck/wiki/Ignore
Veuillez utiliser le suivi des problèmes GitHub pour tout bug ou suggestion de fonctionnalité :
https://github.com/koalaman/shellcheck/issues
Veuillez soumettre les correctifs au code ou à la documentation sous forme de demandes d'extraction GitHub ! Consultez le DevGuide sur le wiki ShellCheck.
Les contributions doivent être sous licence GNU GPLv3. Le contributeur conserve les droits d'auteur.
ShellCheck est sous licence GNU General Public License, v3. Une copie de cette licence est incluse dans le fichier LICENSE.
Copyright 2012-2019, Vidar 'koala_man' Holen et contributeurs.
Bonne vérification Shell !