Warum funktioniert dieses Skript im Terminal, aber nicht aus einer Datei?

18

Ich habe dieses Shell-Skript in einer Datei gespeichert: Es führt einige grundlegende String-Ersetzungen durch.

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

Wenn ich es in die Kommandozeile einfüge, funktioniert es einwandfrei:

$ 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"

gibt

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

Das ist die Ausgabe von Echo oben - es funktioniert wie beabsichtigt.

Aber wenn ich das Skript aufrufe, mit

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

Ich bekomme diese Ausgabe:

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

Verwendet mein Skript eine andere Version von Bash oder so? Muss ich meine Shebang-Linie ändern?

Max Williams
quelle
6
Diese Parametererweiterung ist ein Bashismus (naja, nicht-POSIX): Ändere deinen Schaubang.
Jasonwryan
Vielen Dank! Das hat funktioniert. Ich denke, ich bin ein bisschen verschwommen über den Unterschied zwischen shund bash. Ich werde es nachlesen. Wenn Sie sich die Mühe machen können, Ihren Kommentar in eine Antwort umzuwandeln, werde ich ihn als richtig markieren.
Max Williams
Habe The Duke das Häkchen für seine ausführliche Antwort gegeben, aber trotzdem danke.
Max Williams
2
@MaxWilliams: Kurz gesagt, sh ist die ursprüngliche Borowski-Shell. Alle kompatiblen Skripte sollten für sh geschrieben sein. Aber viele nachfolgende Shells (z. B. bash, Bourne Again-Shell) sind "fast kompatibel" und sorgen für zusätzliche Schönheit. Wie die von Ihnen verwendete Substitution. Heutzutage sind Sie nur mit den posix-Funktionen ziemlich sicher, aber beachten Sie, dass die Portabilität von einer noch engeren Menge abhängt (dh nur sh). Verwenden #!/usr/bin/env bashSie also im Allgemeinen: als Ihren Wahnsinn und verwenden Sie nach Belieben positiv definierte Substitutionen, jedoch mit Einschränkungen in Bezug auf die Portabilität. Und lesen Sie: unix.stackexchange.com/a/48787/27616
Olivier Dulac

Antworten:

36

Was ist sh

sh(oder die Shell Command Language) ist eine Programmiersprache, die vom POSIX-Standard beschrieben wird . Es hat viele Implementierungen ( ksh88, dash, ...). bashkann auch als Implementierung von sh(siehe unten) betrachtet werden.

Da shes sich um eine Spezifikation handelt, nicht um eine Implementierung, /bin/shhandelt es sich um einen Symlink (oder einen festen Link) zu einer tatsächlichen Implementierung auf den meisten POSIX-Systemen.

Was ist Bash?

bashshBegonnen als -kompatible Implementierung (obwohl sie einige Jahre älter ist als der POSIX-Standard), hat sie im Laufe der Zeit jedoch viele Erweiterungen erhalten. Viele dieser Erweiterungen können das Verhalten gültiger POSIX-Shell-Skripte ändern, sodass es sich bashnicht um eine gültige POSIX-Shell handelt. Es ist vielmehr ein Dialekt der POSIX-Shell-Sprache.

bashunterstützt einen --posixSwitch, der POSIX-konformer ist. Es wird auch versucht, POSIX nachzuahmen, wenn es als aufgerufen wird sh.

sh = bash?

Wurde lange Zeit /bin/shverwendet, um /bin/bashauf die meisten GNU / Linux-Systeme zu verweisen . Infolgedessen war es fast sicher, den Unterschied zwischen den beiden zu ignorieren. Aber das hat sich in letzter Zeit geändert.

Einige beliebte Beispiele für Systeme, auf /bin/shdie nicht verwiesen wird /bin/bash(und auf die einige /bin/bashmöglicherweise gar nicht existieren), sind:

  1. Modernes Debian und Ubuntu - Systeme, die ein symbolischer Link shzu dashstandardmäßig;
  2. Busybox , die normalerweise während des Startvorgangs des Linux-Systems ausgeführt wird initramfs. Es wird die ashShell-Implementierung verwendet.
  3. BSDs und im Allgemeinen alle Nicht-Linux-Systeme. OpenBSD verwendet pdksheinen Nachkommen der Korn-Shell. FreeBSD shist ein Abkömmling der ursprünglichen UNIX-Bourne-Shell. Solaris hat eine eigene, shdie lange Zeit nicht POSIX-kompatibel war. Eine kostenlose Implementierung ist im Heirloom-Projekt verfügbar .

Wie können Sie herausfinden, worauf /bin/shIhr System abzielt?

Die Komplikation ist, dass es /bin/shsich um eine symbolische oder eine harte Verbindung handeln kann. Wenn es ein symbolischer Link, ein tragbarer Weg , es zu lösen ist:

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

Wenn es sich um eine harte Verbindung handelt, versuchen Sie es

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

Tatsächlich -Ldeckt das Flag sowohl Symlinks als auch Hardlinks ab, aber der Nachteil dieser Methode ist, dass sie nicht portierbar ist - POSIX muss find die -samefileOption nicht unterstützen , obwohl sowohl GNU find als auch FreeBSD find sie unterstützen.

Shebang Linie

Letztendlich liegt es an Ihnen, zu entscheiden, welche Sie verwenden möchten, indem Sie die Zeile «shebang» schreiben.

Z.B

#!/bin/sh

wird verwenden sh(und auf was auch immer das hinweist),

#!/bin/bash

wird verwendet, /bin/bashwenn es verfügbar ist (und schlägt mit einer Fehlermeldung fehl, wenn dies nicht der Fall ist). Natürlich können Sie auch eine andere Implementierung angeben, z

#!/bin/dash

Welches zu verwenden

Für meine eigenen Skripte bevorzuge ich shaus folgenden Gründen:

  • es ist standardisiert
  • es ist viel einfacher und leichter zu lernen
  • Es ist portabel für POSIX-Systeme - auch wenn es nicht vorhanden ist bash, muss es vorhanden seinsh

Auch die Verwendung bietet Vorteile bash. Seine Funktionen machen das Programmieren bequemer und ähneln dem Programmieren in anderen modernen Programmiersprachen. Dazu gehören lokale Variablen und Arrays mit Gültigkeitsbereich. Plain shist eine sehr minimalistische Programmiersprache.

Hunter.S.Thompson
quelle
Danke für die ausführliche Antwort: Ich verwende Linux Mint, das auf Debian basiert und /bin/shin der Tat ein Symlink zu ist /bin/dash.
Max Williams
Wenn Sie sich an POSIX sh halten möchten, lassen Sie das Wort shebang am besten vollständig aus: Beginnt die erste Zeile einer Datei mit Shell-Befehlen mit den Zeichen "#!", Sind
Daniel Jour
4
@DanielJour Wenn ein Unix-ähnliches System die übliche Unterstützung für Shebangs aufgeben würde, würde es so viele Dinge zerstören, dass es praktisch unbrauchbar wäre. Es ist also ein De-facto-Standard, auch wenn er nicht in POSIX angegeben ist. Das einzige tatsächliche Kompatibilitätsproblem besteht darin, dass der Pfad zu den Dolmetschern unterschiedlich sein kann.
Barmar
23

Ergänzend zu der hervorragenden Antwort von @ Hunter.S.Thompson möchte ich darauf hinweisen, dass der nicht portable Teil des Skripts ist

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

Das ${variable/search/replace}ist eine GNU-Erweiterung. Mit reinem POSIX können Sie dies jedoch leicht vermeiden:

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

Nach Hunter ist dies die bessere Lösung, als den Shebang in zu ändern #! /bin/bash

Philippos
quelle
Danke - ich bin damit einverstanden, dass es die "reinere" Lösung ist, aber ich würde lieber die von shebang geladene Bash verwenden, da Bash das ist, was ich routinemäßig im Terminal verwende (standardmäßig) und es einfacher ist, die Skripte genauso zu machen wie die regulären Befehlszeile.
Max Williams
1
@MaxWilliams Natürlich kein Problem. Dies war hauptsächlich für die Q & A-Datenbank vorgesehen.
Philippos