web-dev-qa-db-fra.com

Pourquoi mon code bash échoue lorsque je l'exécute avec sh?

J'ai une ligne de code qui fonctionne bien dans mon terminal:

for i in *.mp4; do echo ffmpeg -i "$i" "${i/.mp4/.mp3}"; done

Ensuite, je mets exactement la même ligne de code dans un script myscript.sh:

#!/bin/sh
for i in *.mp4; do echo ffmpeg -i "$i" "${i/.mp4/.mp3}"; done

Cependant, maintenant je reçois une erreur lors de son exécution:

$ sh myscript.sh
myscript.sh: 2: myscript.sh: Bad substitution

Sur la base d'autres questions, j'ai essayé de changer le Shebang en #!/bin/bash, mais j'obtiens exactement la même erreur. Pourquoi ne puis-je pas exécuter ce script?

34
WhatIsName

TL; DR: Puisque vous utilisez des fonctionnalités spécifiques de bash, votre script doit s'exécuter avec bash et non avec sh:

$ sh myscript.sh
myscript.sh: 2: myscript.sh: Bad substitution

$ bash myscript.sh
ffmpeg -i bar.mp4 bar.mp3
ffmpeg -i foo.mp4 foo.mp3

Voir Différence entre sh et bash . Pour savoir quel sh vous utilisez: readlink -f $(which sh).

La meilleure façon de s'assurer qu'un script spécifique à bash s'exécute toujours correctement

Les meilleures pratiques consistent à les deux :

  1. Remplacez #!/bin/sh Par #!/bin/bash (Ou tout autre Shell dont dépend votre script).
  2. Exécutez ce script (et tous les autres!) Avec ./myscript.sh Ou /path/to/myscript.sh, Sans sh ou bash.

Voici un exemple:

$ cat myscript.sh
#!/bin/bash
for i in *.mp4
do
  echo ffmpeg -i "$i" "${i/.mp4/.mp3}"
done

$ chmod +x myscript.sh   # Ensure script is executable

$ ./myscript.sh
ffmpeg -i bar.mp4 bar.mp3
ffmpeg -i foo.mp4 foo.mp3

(Connexes: Pourquoi ./ devant les scripts? )

La signification de #!/bin/sh

Le Shebang suggère quel Shell le système devrait utiliser pour exécuter un script. Cela vous permet de spécifier #!/usr/bin/python Ou #!/bin/bash Afin que vous n'ayez pas à vous rappeler quel script est écrit dans quelle langue.

Les gens utilisent #!/bin/sh Lorsqu'ils n'utilisent qu'un ensemble limité de fonctionnalités (définies par la norme POSIX) pour une portabilité maximale. #!/bin/bash Convient parfaitement aux scripts utilisateur qui profitent des extensions bash utiles.

/bin/sh Est généralement lié à un shell compatible POSIX minimal ou à un shell standard (par exemple bash). Même dans ce dernier cas, #!/bin/sh Peut échouer car bash s'exécutera en mode de compatibilité comme expliqué dans la page de manuel :

Si bash est appelé avec le nom sh, il essaie d'imiter le comportement de démarrage des versions historiques de sh aussi fidèlement que possible, tout en se conformant également à la norme POSIX.

La signification de sh myscript.sh

Le Shebang n'est utilisé que lorsque vous exécutez ./myscript.sh, /path/to/myscript.sh, Ou lorsque vous supprimez l'extension, placez le script dans un répertoire de votre $PATH Et exécutez simplement myscript.

Si vous spécifiez explicitement un interpréteur, cet interpréteur sera utilisé. sh myscript.sh Le forcera à fonctionner avec sh, peu importe ce que dit le Shebang. C'est pourquoi changer le Shebang ne suffit pas en soi.

Vous devez toujours exécuter le script avec son interpréteur préféré, donc préférez ./myscript.sh Ou similaire chaque fois que vous exécutez un script.

Autres modifications suggérées à votre script:

  • Il est considéré comme une bonne pratique de citer des variables ("$i" Au lieu de $i). Les variables entre guillemets éviteront les problèmes si le nom de fichier stocké contient des espaces blancs.
  • J'aime que vous utilisiez avancé expansion des paramètres . Je suggère d'utiliser "${i%.mp4}.mp3" (Au lieu de "${i/.mp4/.mp3}"), Car ${parameter%Word} Ne remplace qu'à la fin (par exemple un fichier nommé foo.mp4.backup).
60
Micha Wiedenmann

Le ${var/x/y/} la construction n'est pas POSIX. Dans votre cas, lorsque vous supprimez simplement une chaîne à la fin d'une variable et que vous clouez sur une autre chaîne, la solution POSIX portable consiste à utiliser

#!/bin/sh
for i in *.mp4; do 
    ffmpeg -i "$i" "${i%.mp4}.mp3"
done

ou encore plus court, ffmpeg -i "$i" "${i%4}3".

Le Dope définitif pour ces constructions est le chapitre sur Expansion des paramètres pour le shell POSIX .

10
Jens