Pourquoi ce script fonctionne-t-il dans le terminal mais pas à partir d'un fichier?

19

J'ai ce script shell enregistré dans un fichier: il fait une substitution de chaîne de base.

#!/bin/sh
html_file=$1
echo "html_file = $html_file"
substr=.pdf
pdf_file="${html_file/.html/.pdf}"
echo "pdf_file = $pdf_file"

Si je le colle dans la ligne de commande, cela fonctionne très bien:

$ html_file="/home/max/for_pauld/test_no_base64.html"
  echo "html_file = $html_file"
  substr=.pdf
  pdf_file="${html_file/.html/.pdf}"
  echo "pdf_file = $pdf_file"

donne

html_file = /home/max/for_pauld/test_no_base64.html
pdf_file = /home/max/for_pauld/test_no_base64.pdf

C'est la sortie d'écho ci-dessus - cela fonctionne comme prévu.

Mais, quand j'appelle le script, avec

$ saucer "/home/max/for_pauld/test_no_base64.html"

J'obtiens cette sortie:

html_file = /home/max/for_pauld/test_no_base64.html
/home/max/bin/saucer: 5: /home/max/bin/saucer: Bad substitution

Mon script utilise-t-il une version différente de bash ou quelque chose? Dois-je changer ma ligne de shebang?

Max Williams
la source
6
Cette extension de paramètre est un bashisme (enfin, non-POSIX): changez votre shebang.
jasonwryan
Merci! Ça a marché. Je pense que je suis un peu flou sur la différence entre shet bash. Je vais le lire. Si vous pouvez prendre la peine de faire de votre commentaire une réponse, je le marquerai correctement.
Max Williams
A donné la tique au duc pour sa réponse détaillée, mais merci quand même.
Max Williams
2
@MaxWilliams: en bref, sh est la coquille de bourne d'origine. Tous les scripts compatibles doivent être écrits pour sh. Mais de nombreux shells suivants (ex: bash, Bourne Again shell) sont "presque compatibles" et ajoutent beaucoup de gentillesse supplémentaire. Comme la substitution que vous avez utilisée. De nos jours, vous ne pouvez utiliser que les fonctionnalités posix, mais sachez que la portabilité dépend d'un ensemble encore plus étroit (c'est-à-dire sh uniquement). Donc, en général, utilisez: #!/usr/bin/env bashcomme votre shebang et utilisez des substitutions définies de manière posix comme vous le souhaitez, mais avec des mises en garde de portabilité. Et lisez: unix.stackexchange.com/a/48787/27616
Olivier Dulac

Réponses:

37

Quel est sh

sh(ou Shell Command Language) est un langage de programmation décrit par la norme POSIX . Il a de nombreuses implémentations ( ksh88, dash, ...). bashpeut également être considérée comme une implémentation de sh(voir ci-dessous).

Parce que shc'est une spécification, pas une implémentation, /bin/shest un lien symbolique (ou un lien dur) vers une implémentation réelle sur la plupart des systèmes POSIX.

Qu'est-ce que bash

basha commencé comme une shimplémentation compatible (bien qu'elle soit antérieure au standard POSIX de quelques années), mais avec le temps, elle a acquis de nombreuses extensions. Beaucoup de ces extensions peuvent changer le comportement des scripts shell POSIX valides, donc en soi bashn'est pas un shell POSIX valide. Il s'agit plutôt d'un dialecte du langage shell POSIX.

bashprend en charge un --posixcommutateur, ce qui le rend plus conforme à POSIX. Il essaie également d'imiter POSIX s'il est appelé en tant que sh.

sh = bash?

Pendant longtemps, /bin/shutilisé pour pointer /bin/bashsur la plupart des systèmes GNU / Linux. En conséquence, il était devenu presque sûr d'ignorer la différence entre les deux. Mais cela a commencé à changer récemment.

Voici quelques exemples populaires de systèmes sur lesquels /bin/shne pointe pas /bin/bash(et dont certains /bin/bashpeuvent même ne pas exister):

  1. Les systèmes Debian et Ubuntu modernes, qui ont un lien symbolique shvers dashpar défaut;
  2. Busybox , qui est généralement exécuté pendant le démarrage du système Linux dans le cadre de initramfs. Il utilise l' ashimplémentation du shell.
  3. BSD, et en général tous les systèmes non Linux. OpenBSD utilise pdksh, un descendant du shell Korn. FreeBSD shest un descendant du shell UNIX Bourne d'origine. Solaris a la sienne shqui, pendant longtemps, n'était pas compatible POSIX; une implémentation gratuite est disponible à partir du projet Heirloom .

Comment pouvez-vous savoir ce qui /bin/shpointe sur votre système?

La complication est que cela /bin/shpourrait être un lien symbolique ou un lien dur. S'il s'agit d'un lien symbolique, un moyen portable de le résoudre est:

% file -h /bin/sh
/bin/sh: symbolic link to bash

Si c'est un lien dur, essayez

% find -L /bin -samefile /bin/sh
/bin/sh
/bin/bash

En fait, le -Ldrapeau couvre à la fois les liens symboliques et les liens durs, mais l'inconvénient de cette méthode est qu'elle n'est pas portable - POSIX n'a pas besoin find de prendre en charge l' -samefileoption, bien que GNU find et FreeBSD find la prennent en charge.

Ligne Shebang

En fin de compte, c'est à vous de décider lequel utiliser, en écrivant la ligne «shebang».

Par exemple

#!/bin/sh

utilisera sh(et quoi que ce soit qui pointe),

#!/bin/bash

utilisera /bin/bashs'il est disponible (et échouera avec un message d'erreur s'il ne l'est pas). Bien sûr, vous pouvez également spécifier une autre implémentation, par exemple

#!/bin/dash

Lequel utiliser

Pour mes propres scripts, je préfère shpour les raisons suivantes:

  • il est standardisé
  • c'est beaucoup plus simple et plus facile à apprendre
  • il est portable sur tous les systèmes POSIX - même s’ils n’en ont pas bash, ils doivent avoirsh

Il y a aussi des avantages à utiliser bash. Ses fonctionnalités rendent la programmation plus pratique et similaire à la programmation dans d'autres langages de programmation modernes. Ceux-ci incluent des éléments tels que des variables et des tableaux locaux étendus. Plain shest un langage de programmation très minimaliste.

Hunter.S.Thompson
la source
Merci pour la réponse détaillée: j'utilise Linux Mint qui est basé sur Debian et /bin/shest en effet un lien symbolique vers /bin/dash.
Max Williams
Si vous souhaitez adhérer à POSIX sh, il vaut mieux laisser complètement le shebang: Si la première ligne d'un fichier de commandes shell commence par les caractères "#!", Les résultats sont non spécifiés pubs.opengroup.org/onlinepubs/009695399/ utilitaires /…
Daniel Jour
4
@DanielJour Si un système de type Unix supprimait le support habituel pour les shebangs, il casserait tellement de choses qu'il serait pratiquement inutilisable. C'est donc un standard de facto, même s'il n'est pas spécifié dans POSIX. Le seul problème de compatibilité réel est que le chemin d'accès aux interprètes peut différer.
Barmar
23

Pour ajouter à l'excellente réponse de @ Hunter.S.Thompson, je voudrais souligner que la partie non portable du script est

pdf_file="${html_file/.html/.pdf}"

C'est ${variable/search/replace}une extension GNU. Mais vous pouvez facilement l'éviter avec POSIX pur:

pdf_file="${html_file%.html}".pdf

Après Hunter, c'est la meilleure solution que de changer le shebang en #! /bin/bash

Philippos
la source
Merci - je suis d'accord pour dire que c'est le correctif "le plus pur" mais je préfère le bash chargé par shebang, car bash est ce que j'utilise régulièrement dans le terminal (par défaut) et il est plus facile d'avoir les scripts faire la même chose que le normal ligne de commande.
Max Williams
1
@MaxWilliams Bien sûr, pas de problème. Cela était principalement prévu pour la base de données Q&R.
Philippos