Le script ne fonctionne pas lorsqu’il est exécuté avec ‘#! / Bin / sh’

J’ai un script qui utilise shebang: #!/bin/sh , mais lorsque je l’exécute sur mon terminal Ubuntu 14.04, cela ne fonctionne pas et indique que certaines lignes ont un opérateur inattendu.

Mon ami MacOSX pourrait exécuter ce script en utilisant le #!/bin/sh shebang.

J’ai essayé de changer le shebang en #!/usr/bin/env bash et, tout à coup, le script fonctionne! Qu’est-ce qui se passe ici en fait?

J’espère vraiment que je pourrai également exécuter le !#/bin/sh shebang, de sorte que je n’aurai pas besoin de changer chaque script que j’ai.

Votre ami sur un autre ordinateur utilise probablement un système d’exploitation auquel /bin/sh lié à /bin/bash . Dans Ubuntu (en fait, Debian et la plupart des dérivés de Debian), /bin/sh n’est pas lié à /bin/bash , mais à /bin/dash , qui ne prend pas en charge de nombreuses fonctionnalités spécifiques à bash , mais est considérablement plus rapide.

Sur Arch Linux:

 $ ls -l /bin/sh lrwxrwxrwx 1 root root 4 Sep 28 15:26 /bin/sh -> bash 

Sur Ubuntu:

 $ ls -l /bin/sh lrwxrwxrwx 1 root root 4 Feb 19 2014 /bin/sh -> dash 

La bonne chose à faire

Si vous utilisez un shebang, dites-le. Comme votre script contient bash -isms, utilisez /bin/bash dans le shebang. Ou écrivez un code portable, conforme à POSIX.

Vous pouvez accélérer ce processus en utilisant le programme checkbashisms mentionné dans cet article de LWN . Cela fait partie du paquet devscripts , donc installez-le d’abord:

 sudo apt-get install devscripts 

Ainsi:

 checkbashisms /path/to/script.sh || sed -i '1 s;/bin/sh;/bin/bash;' /path/to/script.sh 

Vous pouvez convertir ceci en un script (par exemple, convert.sh ):

 #! /bin/sh for i do checkbashisms "$i" if [ $? = "1" ] then sed -i '1 s;/bin/sh;/bin/bash;' "$i" fi done 

Le code de retour spécifique de 1 signifie que checkbashisms trouvé un bash possible, et que les autres valeurs de retour indiquent d’autres problèmes (fichier non lisible, fichier manquant, etc.), ce qui permet de rechercher cette valeur de retour particulière.

Et puis appelez-le avec:

 ./convert.sh /path/to/first/script.sh /path/to/second/script.sh # or ./convert.sh *.sh # or find . -iname '*.sh' -exec ./convert.sh {} + 

La mauvaise chose à faire

Remplacez /bin/sh par un lien symbolique vers /bin/bash .

Lecture recommandée:

  • Bashism – compare la syntaxe bash et dash et indique les modifications nécessaires.
  • Dash comme /bin/sh – similaire.

Dans mon cas, le problème était l’exportation de la définition de variable IFS dans ~ / .bashrc (~ / .profile)

 #Loop over filenames with spaces export IFS=$'\n' 

Lorsque changé pour soit
export IFS=$' \t\n' #default according to man dash
ou
IFS=$'\n'
Les scripts #! / bin / sh ont recommencé à fonctionner. Dans ce dernier cas, la définition ne se propage plus aux processus forked / bin / sh. La signification de IFS est expliquée ici et sa définition sur ‘\ n’ pose des problèmes dans les scripts interprétés par dash shell, qui peut être le shell par défaut /bin/sh -> dash associé à votre dissortingbution (par exemple, debian).