Écriture de scripts shell qui s'exécuteront sur n'importe quel shell (en utilisant plusieurs lignes de shebang?)

19

Je viens de commencer à approfondir les scripts shell, et je viens de jeter mon script dans un fichier, de le marquer chmod +x, puis de le faire /path/to/script.shet de laisser le choix de l'interpréteur par défaut, ce que j'ai supposé être zsh parce que c'est ce que J'ai utilisé pour ma coquille. Apparemment, il semble que ce soit juste /bin/shpar défaut, même si j'exécute le script à partir d'une invite zsh, parce que j'ai commencé à mettre des choses spécifiques à zsh dans mes scripts et qu'il échoue à moins que je n'exécute zsh /path/to/script.sh.

Pour en venir au fait, voici mes questions:

  1. Quel shell exécute les scripts quand il n'y a pas de ligne shebang ( #!/path/to/shell) au début? Je suppose, /bin/shmais je ne peux pas confirmer.
  2. Quelles sont les «meilleures pratiques» en termes d'écriture de scripts shell qui s'exécuteront sur n'importe quelle plate-forme? (ok, c'est en quelque sorte ouvert)
  3. Est-il possible d'écrire un script qui essaie d'utiliser zsh et retombe sur bash si zsh n'est pas disponible? J'ai essayé de mettre deux lignes de shebang, comme ci-dessous, mais ça ne fait que des erreurs bad interpreter: /bin/zsh: no such file or directorysi je l'essaye sur une machine sans zsh.

    #!/bin/zsh

    #!/bin/bash

swrobel
la source

Réponses:

26

Quel shell exécute les scripts quand il n'y a pas de ligne shebang (#! / Path / to / shell) au début? Je suppose que / bin / sh mais je ne peux pas confirmer.

Le noyau refuse d'exécuter ces scripts et retourne ENOEXEC, de sorte que le comportement exact dépend du programme que vous exécutez un tel script à partir .

  • bash 4.2.39 - utilise lui-même
  • busybox-ash 1.20.2 - utilise lui-même
  • dash 0.5.7 - runs / bin / sh
  • fish 1.23.1 - se plaint d'ENOEXEC, puis blâme le mauvais fichier
  • AT&T ksh 93u + 2012.08.01 - utilise lui-même
  • mksh R40f - s'exécute / bin / sh
  • pdksh 5.2.14 - exécute / bin / sh
  • sh-heirloom 050706 - utilise lui-même
  • tcsh 6.18.01 - exécute / bin / sh
  • zsh 5.0.0 - exécute / bin / sh
  • cmd.exe 5.1.2600 - vous regarde drôle.

Dans la glibc , les fonctions execv()ou execve()simplement retournent ENOEXEC. Mais execvp()cache ce code d'erreur et invoque automatiquement / bin / sh. (Ceci est documenté dans exec (3p) .)

Quelles sont les «meilleures pratiques» en termes d'écriture de scripts shell qui s'exécuteront sur n'importe quelle plate-forme? (ok, c'est en quelque sorte ouvert)

Soit vous vous en tenez aux shseules fonctionnalités définies par POSIX, soit vous vous contentez d'aller à bash complet (qui est largement disponible) et de le mentionner dans vos besoins si vous le distribuez.

(Maintenant que j'y pense, Perl - ou peut-être Python - serait encore plus portable, sans parler d'avoir une meilleure syntaxe.)

Ajoutez toujours la ligne Shebang. Si vous utilisez bash ou zsh, utilisez #!/usr/bin/env bashau lieu de coder en dur le chemin du shell. (Cependant, le shell POSIX est garanti sur /bin/sh, donc sautez envdans ce cas.)

(Malheureusement, even /bin/shn'est pas toujours le même. Le programme GNU autoconf doit gérer de nombreuses bizarreries différentes .)

Est-il possible d'écrire un script qui essaie d'utiliser zsh et retombe sur bash si zsh n'est pas disponible? J'ai essayé de mettre deux lignes de shebang, comme ci-dessous, mais ça ne fait que des erreurs avec un mauvais interprète: / bin / zsh: aucun fichier ou répertoire de ce type si je l'essaie sur une machine sans zsh.

Il ne peut y avoir qu'une seule ligne de shebang; tout ce qui se trouve après le caractère de nouvelle ligne n'est même pas lu par le noyau et traité comme un commentaire par des shells.

Il est possible d'écrire un script qui s'exécute en tant que #!/bin/sh, vérifie quel shell est disponible et s'exécute exec zsh "$0" "$@"ou en exec bash "$0" "$@"fonction du résultat. Cependant, la syntaxe utilisée par bash et zsh est si différente à divers endroits que je ne recommanderais pas de le faire pour votre propre raison.

grawity
la source
1
comment avez-vous suivi le nom de chaque shell lorsqu'il a reçu ENOEXEC?
swrobel
2
@SWrobel: Utilisation strace -f -e fork,clone,execve. Certains obus ont continué à être exécutés /bin/shaprès l'échec; d'autres ont interprété le script eux-mêmes.
grawity
1
@SWrobel: Une autre méthode consiste à exécuter un script composé de readlink /proc/$$/exe.
grawity
2
Voir aussi la réponse de Stéphane Chazelas à Quel interprète shell exécute un script sans shebang? (duplicata intersite de la sous-question # 1)
G-Man dit 'Reinstate Monica' le
1
Pour cshet tcsh, le comportement dépend du début du script #(auquel cas ils s’invoquent au lieu de sh pour interpréter le script). Cela remonte à l'époque où csh supportait les commentaires mais pas le shell Bourne, donc #c'était un indice qu'il s'agissait d'un script csh.
sch
2

1) Le shell actuel dans lequel vous utilisez. (Quel que soit le shell)

2) Restez avec le même type de shell (bash / dash / ash / csh / quelle que soit votre saveur) et assurez-vous que vos "plates-formes prises en charge" installent le shell que vous souhaitez utiliser par défaut. Essayez également d'utiliser les commandes couramment disponibles sur les systèmes. Évitez les options spécifiques à la machine.

3) Il n'y a pas vraiment de logique "si-alors-autre" dans le interpreter directive. Vous devez spécifier un shell qui devrait exister sur tous les systèmes que vous souhaitez prendre en charge ... c'est #!/bin/bash-à- dire ou spécifier un générique #!/bin/shtant que votre script est assez générique sur tous les shells.

TheCompWiz
la source