ShellCheck Análisis Estático Scripts Shell
ShellCheck Análisis Estático Scripts Shell

ShellCheck: Análisis Estático para Scripts de Shell

ShellCheck es una herramienta GPLv3 que ofrece advertencias y sugerencias para scripts de shell bash/sh:

screenshot ShellCheck
screenshot ShellCheck

Las metas de ShellCheck son

  • Para señalar y aclarar los típicos problemas de sintaxis de los issues hacen que un shell produzca mensajes de error crípticos.
  • Para señalar y aclarar los típicos problemas semánticos de nivel intermedio que hacen que shell se comporte de forma extraña y contraintuitiva.
  • Señalar las sutiles advertencias, los casos de esquina que pueden hacer que un script de un usuario avanzado que funcione de otra manera falle en circunstancias futuras.

Mira la galería de código erróneo para ver ejemplos de lo que ShellCheck puede ayudarte a identificar!

Cómo Usar

Hay varias maneras de usar ShellCheck!

En la web

Pega un script de shell en https://www.shellcheck.net para obtener una respuesta instantánea.

ShellCheck.net siempre está sincronizado con el último git commit, y es la forma más fácil de probar ShellCheck. Cuéntale a tus amigos!

Desde tu terminal

Ejecuta  shellcheck tuscript en tu terminal para obtener una salida instantánea, como has visto arriba.

En tu editor

Puedes ver las sugerencias de ShellCheck directamente en una variedad de editores.

screenshot vim syntastic
screenshot vim syntastic
screenshot emacs flycheck
screenshot emacs flycheck

En tus suites de construcción o de prueba

Aunque ShellCheck está pensado sobre todo para un uso interactivo, puede añadirse fácilmente a las construcciones o a las suites de pruebas. Hace un uso canónico de los códigos de salida, así que puedes añadir un comando shellcheck como parte del proceso.

Por ejemplo, en un Makefile:

check-scripts:
    # Fail if any of these files have warnings
    shellcheck myscripts/*.sh

O en un archivo Travis CI .travis.yml:

script:
  # Fail if any of these files have warnings
  - shellcheck myscripts/*.sh

Servicios y plataformas que tienen ShellCheck preinstalado y listo para usar:

Servicios y plataformas con plugins de terceros:

La mayoría de los demás servicios, incluido GitLab, permiten instalar ShellCheck por sí mismos, ya sea a través del administrador de paquetes del sistema (ver Installing), o descargando y desempaquetando un binary release.

Es una buena idea instalar manualmente una versión específica de ShellCheck de todas formas. Así se evitan las sorpresivas interrupciones en la construcción cuando se publica una nueva versión con nuevas advertencias.

Para filtrar o informar de forma personalizada, ShellCheck puedes producir simples JSON, XML compatible con CheckStyle, advertencias compatibles con GCC, así como texto legible para el ser humano (con o sin colores ANSI). Consulta la página wiki de Integration para obtener más documentación.

Instalación

La forma más fácil de instalar ShellCheck localmente es a través de tu administrador de paquetes.

En los sistemas con Cabal (se instala en  ~/.cabal/bin):

stack update
stack install ShellCheck

En distros basados en Debian:

apt-get install shellcheck

En distros basados en Arch Linux:

pacman -S shellcheck

O conseguir la dependencia libre shellcheck-bin del AUR.

En distros basados en Gentoo:

emerge --ask shellcheck

En distros basados en EPEL:

yum -y install epel-release
yum install ShellCheck

En distros basados en Fedora:

dnf install ShellCheck

En FreeBSD:

pkg install hs-ShellCheck

En macOS (OS X) con Homebrew:

brew install shellcheck

En OpenBSD:

pkg_add shellcheck

En openSUSE

zypper in ShellCheck

O utiliza OneClickInstall – https://software.opensuse.org/package/ShellCheck

En Solus:

eopkg install shellcheck

En Windows (vía chocolatey):

C:\> choco install shellcheck

En Windows (vía scoop):

C:\> scoop install shellcheck

De Snap Store:

snap install --channel=edge shellcheck

De Docker Hub:

docker run --rm -v "$PWD:/mnt" koalaman/shellcheck:stable myscript
# Or :v0.4.7 for that version, or :latest for daily builds

o usa koalaman/shellcheck-alpine si quieres que se extienda una imagen más grande basada en Alpine Linux. Funciona exactamente como una imagen Alpine normal, pero tiene Shellcheck preinstalado.

Usando el administrador de paquetes nix::

nix-env -iA nixpkgs.shellcheck

Alternativamente, puedes descargar binarios pre-compilados para la última versión aquí:

o ver la página GitHub Releases para otros lanzamientos (incluyendo el último meta-release para la construcción diaria de git).

Los paquetes de los distros vienen ya con una página de manual. Si está construyendo desde la fuente, se puede instalar con:

pandoc -s -f markdown-smart -t man shellcheck.1.md -o shellcheck.1
sudo mv shellcheck.1 /usr/share/man/man1

Travis CI

Travis CI ha integrado ShellCheck por defecto, así que no es necesario instalarlo manualmente.

Si aún así quieres hacerlo para actualizar a tu gusto o asegurarte de que estás usando la última versión, sigue los pasos que se indican a continuación para instalar una versión binaria.

Instalando un binario pre-compilado

Los binarios precompilados vienen en documentos tar.xz. Para descomprimirlos, asegúrate de que xz esté instalado. En Debian/Ubuntu/Mint, puedes instalar aptos para install xz-utils. On Redhat/Fedora/CentOS, yum -y install xz.

Un simple instalador puede hacer algo como:

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

Compilación de la fuente

Esta sección describe cómo construir ShellCheck desde un directorio de origen. ShellCheck está escrito en Haskell y requiere 2GB de RAM para compilar.

Instalando Cabal

ShellCheck está construido y empaquetado usando Cabal. Instala el paquete cabal-install del administrador de paquetes de su sistema (con e.g. apt-get, brewemergeyum, o zypper).

En macOS (OS X), puedes hacer una instalación rápida de Cabal usando brew, lo cual toma un par de minutos en lugar de más de 30 minutos si tratas de compilarlo desde la fuente.

$ brew install cabal-install

En MacPorts, el paquete se llama en cambio hs-cabal-install, mientras que los usuarios nativos de Windows deben instalar la última versión de la plataforma de Haskell desde https://www.haskell.org/platform/

Verifica que Cabal esté instalada y actualiza su lista de dependencias con

$ cabal update

Compilando ShellCheck

git clone este repositorio, y cd al directorio de origen de ShellCheck para construir/instalar:

$ cabal install

O si tienes la intención de hacer las pruebas:

$ cabal install --enable-tests

Esto compilará ShellCheck y lo instalará en tu directorio ~/.cabal/bin .

Añade este directorio a tu PATH (para bash, añade esto a tu ~/.bashrc):

export PATH="$HOME/.cabal/bin:$PATH"

Cierra la sesión y vuelve a entrar, y verifica que tu PATH esté configurado correctamente:

$ which shellcheck
~/.cabal/bin/shellcheck

En Windows nativo, el PATH ya debería estar configurado, pero el sistema puede utilizar una página de códigos heredada. En cmd.exepowershell.exe y Powershell ISE, asegúrate de usar una fuente TrueType, no una fuente Raster, y establece la página de códigos activa en UTF-8 (65001) con chcp:

chcp 65001

En Powershell ISE, puede que tengas que actualizar adicionalmente la codificación de salida:

[Console]::OutputEncoding = [System.Text.Encoding]::UTF8

Realizando pruebas

Para ejecutar el conjunto de pruebas de la unidad:

$ cabal test

Galería de código malo

Entonces, ¿qué tipo de cosas busca ShellCheck? Aquí hay una lista incompleta de las issues detectadas.

Quoting

ShellCheck puede reconocer varios tipos de citas incorrectas:

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

Condicionales

ShellCheck puede reconocer muchos tipos de declaraciones de pruebas incorrectas.

[[ 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 ((..))

Comandos frecuentemente mal utilizados

ShellCheck puede reconocer los casos en los que los comandos se utilizan de forma incorrecta:

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

Errores comunes de los principiantes

ShellCheck reconoce muchos errores de sintaxis comunes de los principiantes:

 = 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 $10"         # 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

Estilo

ShellCheck puede hacer sugerencias para mejorar el estilo:

[[ -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

Datos y errores de escritura

ShellCheck puede reconocer problemas relacionados con los datos y la escritura:

args="$@"                         # Assigning arrays to strings
files=(foo bar); echo "$files"    # Referencing arrays as strings
declare -A arr=(foo bar)          # Associative arrays without index
printf "%s\n" "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: %s\n' foo             # Mismatches in printf argument count

Robustez

ShellCheck puede hacer sugerencias para mejorar la robustez de 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

Portabilidad

ShellCheck avisará cuando se usen características no soportadas por el shebang. Por ejemplo, si configuras el shebang a #!/bin/sh, ShellCheck advertirá sobre problemas de portabilidad similares a 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
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'

Miscelánea

ShellCheck reconoce una colección de otros issues:

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
while getopts "a" f; do case $f in "b") # Unhandled getopts flags

Testimonios

Al principio eres como “shellcheck es impresionante” pero luego eres como “wtf seguimos usando bash”

Alexander Tarasikov, via Twitter

Ignorando issues

Los Issues pueden ser ignorados a través de la variable ambiental, línea de comando, individualmente o globalmente dentro de un archivo: leer aquí.

Informar de los errores

Por favor, usa la sección de issue de GitHub para cualquier error o sugerencia de características.

Contribuir

Por favor, envía los patches al código o la documentación que GitHub solicite. Echa un vistazo a la DevGuide en la Wiki de ShellCheck.

Las contribuciones deben estar licenciadas bajo la GPLv3 de GNU. El colaborador conserva los derechos de autor.

Copyright

ShellCheck está licenciado bajo la Licencia Pública General de GNU, v3. Una copia de esta licencia está incluida en el archivo LICENSE.

Copyright 2012-2019, Vidar ‘koala_man’ Holen y contribuyentes.

Otros recursos

  • Wiki tiene largas descripciones para cada advertencia, p.e. SC2221.
  • ShellCheck no intenta imponer ningún tipo de formato o estilo de sangría, así que también comprueba shfmt!

Más artículos
Servicios de Traducción Profesional Protranslate
Servicios de Traducción Profesional a un clic de distancia