shellcheck-configuration

Par wshobson · agents

Maîtrisez la configuration et l'utilisation de l'analyse statique ShellCheck pour la qualité des scripts shell. À utiliser lors de la mise en place d'une infrastructure de linting, de la correction de problèmes de code ou de la vérification de la portabilité des scripts.

npx skills add https://github.com/wshobson/agents --skill shellcheck-configuration

Configuration et Analyse Statique avec ShellCheck

Guide complet pour configurer et utiliser ShellCheck afin d'améliorer la qualité des scripts shell, détecter les pièges courants et appliquer les bonnes pratiques par l'analyse statique du code.

Quand utiliser ce skill

  • Configurer le linting des scripts shell dans les pipelines CI/CD
  • Analyser les scripts shell existants pour détecter les problèmes
  • Comprendre les codes d'erreur et avertissements de ShellCheck
  • Configurer ShellCheck selon les exigences du projet
  • Intégrer ShellCheck dans les workflows de développement
  • Supprimer les faux positifs et configurer les ensembles de règles
  • Appliquer des standards de qualité de code cohérents
  • Migrer les scripts pour respecter les critères de qualité

Principes Fondamentaux de ShellCheck

Qu'est-ce que ShellCheck ?

ShellCheck est un outil d'analyse statique qui examine les scripts shell et détecte les motifs problématiques. Il supporte :

  • Bash, sh, dash, ksh et autres shells POSIX
  • Plus de 100 avertissements et erreurs différents
  • Configuration pour le shell cible et ses flags
  • Intégration avec les éditeurs et systèmes CI/CD

Installation

# macOS avec Homebrew
brew install shellcheck

# Ubuntu/Debian
apt-get install shellcheck

# Depuis les sources
git clone https://github.com/koalaman/shellcheck.git
cd shellcheck
make build
make install

# Vérifier l'installation
shellcheck --version

Fichiers de Configuration

.shellcheckrc (Niveau Projet)

Créez .shellcheckrc à la racine de votre projet :

# Spécifier le shell cible
shell=bash

# Activer les vérifications optionnelles
enable=avoid-nullary-conditions
enable=require-variable-braces

# Désactiver les avertissements spécifiques
disable=SC1091
disable=SC2086

Variables d'Environnement

# Définir le shell cible par défaut
export SHELLCHECK_SHELL=bash

# Activer le mode strict
export SHELLCHECK_STRICT=true

# Spécifier l'emplacement du fichier de configuration
export SHELLCHECK_CONFIG=~/.shellcheckrc

Codes d'Erreur ShellCheck Courants

SC1000-1099 : Erreurs d'Analyse

# SC1004: Continuation avec antislash non suivie d'une nouvelle ligne
echo hello\
world  # Erreur - nécessite une continuation de ligne

# SC1008: Données invalides pour l'opérateur `=='
if [[ $var =  "value" ]]; then  # Espace avant ==
    true
fi

SC2000-2099 : Problèmes Shell

# SC2009: Envisager d'utiliser pgrep ou pidof au lieu de grep|grep
ps aux | grep -v grep | grep myprocess  # Utiliser pgrep à la place

# SC2012: Utiliser `ls` uniquement pour l'affichage. Utiliser `find` pour une sortie fiable
for file in $(ls -la)  # Mieux : utiliser find ou globbing

# SC2015: Éviter d'utiliser && et || au lieu de if-then-else
[[ -f "$file" ]] && echo "found" || echo "not found"  # Moins clair

# SC2016: Les expressions ne se développent pas entre guillemets simples
echo '$VAR'  # Littéral $VAR, pas développement de variable

# SC2026: Ce mot n'est pas standard. Définir POSIXLY_CORRECT
# lors de l'utilisation avec des scripts pour d'autres shells

SC2100-2199 : Problèmes de Guillemets

# SC2086: Ajouter des guillemets doubles pour éviter le globbing et le fractionnement de mots
for i in $list; do  # Devrait être : for i in $list ou for i in "$list"
    echo "$i"
done

# SC2115: Tilde littéral dans le chemin non développé. Utiliser $HOME à la place
~/.bashrc  # Dans les chaînes, utiliser "$HOME/.bashrc"

# SC2181: Vérifier le code de sortie directement avec `if`, pas indirectement dans une liste
some_command
if [ $? -eq 0 ]; then  # Mieux : if some_command; then

# SC2206: Ajouter des guillemets pour éviter le fractionnement de mots ou définir IFS
array=( $items )  # Devrait utiliser : array=( $items )

SC3000-3999 : Problèmes de Conformité POSIX

# SC3010: Dans POSIX sh, utiliser 'case' au lieu de 'cond && foo'
[[ $var == "value" ]] && do_something  # Pas POSIX

# SC3043: Dans POSIX sh, 'local' n'est pas défini
function my_func() {
    local var=value  # Pas POSIX dans certains shells
}

Exemples de Configuration Pratiques

Configuration Minimale (POSIX Strict)

#!/bin/bash
# Configurer pour la portabilité maximale

shellcheck \
  --shell=sh \
  --external-sources \
  --check-sourced \
  script.sh

Configuration de Développement (Bash avec Règles Relaxées)

#!/bin/bash
# Configurer pour le développement Bash

shellcheck \
  --shell=bash \
  --exclude=SC1091,SC2119 \
  --enable=all \
  script.sh

Configuration d'Intégration CI/CD

#!/bin/bash
set -Eeuo pipefail

# Analyser tous les scripts shell et échouer en cas de problème
find . -type f -name "*.sh" | while read -r script; do
    echo "Checking: $script"
    shellcheck \
        --shell=bash \
        --format=gcc \
        --exclude=SC1091 \
        "$script" || exit 1
done

.shellcheckrc pour Projet

# Dialecte shell à analyser
shell=bash

# Activer les vérifications optionnelles
enable=avoid-nullary-conditions,require-variable-braces,check-unassigned-uppercase

# Désactiver les avertissements spécifiques
# SC1091: Ne pas suivre les fichiers sourcés (nombreux faux positifs)
disable=SC1091

# SC2119: Utiliser function_name au lieu de function_name -- (arguments)
disable=SC2119

# Fichiers externes à sourcer pour le contexte
external-sources=true

Modèles d'Intégration

Configuration de Hook Pre-commit

#!/bin/bash
# .git/hooks/pre-commit

#!/bin/bash
set -e

# Trouver tous les scripts shell modifiés dans ce commit
git diff --cached --name-only | grep '\.sh$' | while read -r script; do
    echo "Linting: $script"

    if ! shellcheck "$script"; then
        echo "ShellCheck failed on $script"
        exit 1
    fi
done

Workflow GitHub Actions

name: ShellCheck

on: [push, pull_request]

jobs:
  shellcheck:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Run ShellCheck
        run: |
          sudo apt-get install shellcheck
          find . -type f -name "*.sh" -exec shellcheck {} \;

Pipeline GitLab CI

shellcheck:
  stage: lint
  image: koalaman/shellcheck-alpine
  script:
    - find . -type f -name "*.sh" -exec shellcheck {} \;
  allow_failure: false

Gérer les Violations ShellCheck

Supprimer les Avertissements Spécifiques

#!/bin/bash

# Désactiver l'avertissement pour toute la ligne
# shellcheck disable=SC2086
for file in $(ls -la); do
    echo "$file"
done

# Désactiver pour tout le script
# shellcheck disable=SC1091,SC2119

# Désactiver plusieurs avertissements (le format varie)
command_that_fails() {
    # shellcheck disable=SC2015
    [ -f "$1" ] && echo "found" || echo "not found"
}

# Désactiver la vérification spécifique pour la directive source
# shellcheck source=./helper.sh
source helper.sh

Violations Courantes et Corrections

SC2086 : Ajouter des guillemets doubles pour éviter le fractionnement de mots

# Problème
for i in $list; do done

# Solution
for i in $list; do done  # Si $list est déjà entre guillemets, ou
for i in "${list[@]}"; do done  # Si list est un tableau

SC2181 : Vérifier le code de sortie directement

# Problème
some_command
if [ $? -eq 0 ]; then
    echo "success"
fi

# Solution
if some_command; then
    echo "success"
fi

SC2015 : Utiliser if-then au lieu de && ||

# Problème
[ -f "$file" ] && echo "exists" || echo "not found"

# Solution - intention plus claire
if [ -f "$file" ]; then
    echo "exists"
else
    echo "not found"
fi

SC2016 : Les expressions ne se développent pas entre guillemets simples

# Problème
echo 'Variable value: $VAR'

# Solution
echo "Variable value: $VAR"

SC2009 : Utiliser pgrep au lieu de grep

# Problème
ps aux | grep -v grep | grep myprocess

# Solution
pgrep -f myprocess

Optimisation des Performances

Vérifier Plusieurs Fichiers

#!/bin/bash

# Vérification séquentielle
for script in *.sh; do
    shellcheck "$script"
done

# Vérification parallèle (plus rapide)
find . -name "*.sh" -print0 | \
    xargs -0 -P 4 -n 1 shellcheck

Mise en Cache des Résultats

#!/bin/bash

CACHE_DIR=".shellcheck_cache"
mkdir -p "$CACHE_DIR"

check_script() {
    local script="$1"
    local hash
    local cache_file

    hash=$(sha256sum "$script" | cut -d' ' -f1)
    cache_file="$CACHE_DIR/$hash"

    if [[ ! -f "$cache_file" ]]; then
        if shellcheck "$script" > "$cache_file" 2>&1; then
            touch "$cache_file.ok"
        else
            return 1
        fi
    fi

    [[ -f "$cache_file.ok" ]]
}

find . -name "*.sh" | while read -r script; do
    check_script "$script" || exit 1
done

Formats de Sortie

Format par Défaut

shellcheck script.sh

# Sortie :
# script.sh:1:3: warning: foo is referenced but not assigned. [SC2154]

Format GCC (pour CI/CD)

shellcheck --format=gcc script.sh

# Sortie :
# script.sh:1:3: warning: foo is referenced but not assigned.

Format JSON (pour l'analyse)

shellcheck --format=json script.sh

# Sortie :
# [{"file": "script.sh", "line": 1, "column": 3, "level": "warning", "code": 2154, "message": "..."}]

Format Silencieux

shellcheck --format=quiet script.sh

# Retourne un code non-zéro si des problèmes sont trouvés, pas de sortie sinon

Bonnes Pratiques

  1. Exécuter ShellCheck dans CI/CD - Détecter les problèmes avant la fusion
  2. Configurer pour votre shell cible - Ne pas analyser bash comme sh
  3. Documenter les exclusions - Expliquer pourquoi les violations sont supprimées
  4. Résoudre les violations - Ne pas simplement désactiver les avertissements
  5. Activer le mode strict - Utiliser --enable=all avec exclusions soigneuses
  6. Mettre à jour régulièrement - Maintenir ShellCheck à jour pour les nouvelles vérifications
  7. Utiliser les hooks pre-commit - Détecter les problèmes localement avant de pousser
  8. Intégrer avec les éditeurs - Obtenir un retour en temps réel pendant le développement

Skills similaires