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?
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)
.
Les meilleures pratiques consistent à les deux :
#!/bin/sh
Par #!/bin/bash
(Ou tout autre Shell dont dépend votre script)../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? )
#!/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.
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.
"$i"
Au lieu de $i
). Les variables entre guillemets éviteront les problèmes si le nom de fichier stocké contient des espaces blancs."${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
).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 .