#! / bin / sh vs #! / bin / bash voor maximale draagbaarheid

cherouvim 07/30/2017. 3 answers, 2.886 views
linux bash shell sh

Ik werk meestal met Ubuntu LTS-servers die ik begrijp van symlink /bin/sh naar /bin/dash . Veel andere distros via symlink /bin/sh naar /bin/bash .

Daaruit begrijp ik dat als een script #!/bin/sh bovenaan gebruikt het misschien niet op alle servers op dezelfde manier werkt?

Is er een voorgestelde praktijk om shell te gebruiken voor scripts wanneer men maximale portabiliteit van die scripts tussen servers wil?

1 Comments
Thorbjørn Ravn Andersen 08/01/2017
Er zijn kleine verschillen tussen de verschillende shells. Als draagbaarheid voor u het belangrijkst is, gebruikt u #!/bin/sh en gebruikt u niets anders dan wat de originele shell heeft verstrekt.

3 Answers


Gordon Davisson 08/01/2017.

Er zijn grofweg vier niveaus van portabiliteit voor shell-scripts (wat betreft de shebang-regel):

  1. Meest draagbare: gebruik een #!/bin/sh shebang en gebruik only de basishell-syntaxis die is opgegeven in de POSIX-standaard . Dit zou op vrijwel elk POSIX / unix / linux-systeem moeten werken. (Nou, behalve Solaris 10 en eerder die de echte oude Bourne-shell hadden, vóór POSIX dus niet compliant, als /bin/sh .)

  2. Tweede meest draagbare: gebruik een #!/bin/bash (of #!/usr/bin/env bash ) shebang-regel en blijf bij bash v3-functies. Dit werkt op elk systeem met bash (op de verwachte locatie).

  3. Derde meest draagbaar: gebruik een #!/bin/bash (of #!/usr/bin/env bash ) shebang-regel en gebruik bash v4-functies. Dit zal mislukken op elk systeem dat bash v3 heeft (bijv. MacOS, dat het om licentieredenen moet gebruiken).

  4. Minst draagbaar: gebruik een #!/bin/sh shebang en gebruik bash-extensies voor de POSIX shell-syntaxis. Dit zal mislukken op elk systeem dat iets anders heeft dan bash voor / bin / sh (zoals recente Ubuntu-versies). Doe dit nooit; het is niet alleen een compatibiliteitsprobleem, het is gewoon fout. Helaas is het een fout die veel mensen maken.

Mijn aanbeveling: gebruik de meest conservatieve van de eerste drie die alle shell-functies levert die u voor het script nodig hebt. Voor maximale draagbaarheid, gebruik optie # 1, maar in mijn ervaring zijn sommige bash-functies (zoals arrays) nuttig genoeg om met # 2 mee te gaan.

Het ergste wat je kunt doen is # 4, met behulp van de verkeerde shebang. Als je niet zeker weet welke functies standaard POSIX zijn en welke bash-extensies zijn, gebruik dan een bash shebang (dwz optie # 2), of test het script grondig met een erg eenvoudige shell (zoals een streepje op je Ubuntu LTS-servers). De Ubuntu-wiki heeft een goede lijst van bashismen om op te letten .

Er is een aantal zeer goede informatie over de geschiedenis en verschillen tussen shells in de Unix & Linux-vraag: "Wat betekent het om sh-compatibel te zijn?" en de Stackoverflow-vraag "Verschil tussen sh en bash" .

Houd er ook rekening mee dat de shell niet het enige is dat verschilt tussen verschillende systemen; als je Linux gewend bent, ben je gewend aan de GNU-commando's, die veel niet-standaard extensies hebben die je misschien niet op andere Unix-systemen vindt (bijv. bsd, macOS). Helaas is hier geen eenvoudige regel, je hoeft alleen maar het variatiebereik te kennen voor de commando's die je gebruikt.

Een van de smerigste opdrachten in termen van draagbaarheid is een van de meest elementaire: echo . Elke keer dat u het gebruikt met opties (bijv. echo -n of echo -e ), of met eventuele escapes (backslashes) in de reeks om af te drukken, zullen verschillende versies verschillende dingen doen. Elke keer dat u een string wilt afdrukken zonder een linefeed erna of met escapes in de string, gebruikt u printf plaats daarvan (en leert u hoe het werkt - het is gecompliceerder dan echo is). Het ps commando is ook een puinhoop .

Een ander algemeen ding om op te letten is recente / GNUish-uitbreidingen voor de syntaxis van opdrachtopties: de oude (standaard) opdrachtindeling is dat de opdracht wordt gevolgd door opties (met een enkel streepje en elke optie is een enkele letter), gevolgd door commando argumenten. Recente (en vaak niet-draagbare) varianten bevatten lange opties (meestal geïntroduceerd met -- ), waardoor opties na argumenten komen en -- om opties van argumenten te scheiden.

5 comments
12 pabouk 07/30/2017
De vierde optie is gewoon een verkeerd idee. Gebruik het alstublieft niet.
3 Gordon Davisson 07/30/2017
@ pabouk Ik ben het er volledig mee eens, dus ik heb mijn antwoord bewerkt om dit duidelijker te maken.
1 jlliagre 07/30/2017
Je eerste verklaring is enigszins misleidend. De POSIX-standaard specificeert niets over de shebang buiten te vertellen gebruik ervan leidt tot niet-gespecificeerd gedrag. Bovendien geeft POSIX niet aan waar de posix-shell zich moet bevinden, alleen de naam ( sh ), dus /bin/sh is niet gegarandeerd het juiste pad. Het meest draagbare is om helemaal geen shebang te specificeren, of om de shebang aan te passen aan het gebruikte besturingssysteem.
2 Michael Kjörling 07/30/2017
Ik viel heel recent in # 4 met een script van mij, en kon gewoon niet achterhalen waarom het niet werkte; immers, dezelfde commando's werkten goed, en deden precies wat ik wilde dat ze deden, toen ik ze direct in de shell probeerde. Zodra ik #!/bin/sh in #!/bin/bash , werkte het script echter perfect. (Tot mijn verdediging was dat script in de loop van de tijd geëvolueerd van iemand die eigenlijk alleen maar sh-isms nodig had, een script dat zich baseerde op bash-achtig gedrag.)
2 jlliagre 07/30/2017
@ MichaelKjörling De (ware) Bourne-shell wordt bijna nooit gebundeld met Linux-distributies en is toch niet POSIX-compatibel. De standaard POSIX-shell is gemaakt op basis van ksh gedrag, niet van Bourne. Wat de meeste Linux-distributies die de FHS volgen doen, is dat /bin/sh een symbolische link is naar de shell die ze selecteren om POSIX-compatibiliteit te bieden, meestal bash of dash .

Kaz 07/31/2017.

In het ./configure dat de TXR-taal voorbereidt voor het bouwen, heb ik de volgende proloog geschreven voor een betere draagbaarheid. Het script bootstrap zichzelf zelfs als #!/bin/sh een niet-POSIX-conforme oude Bourne Shell is. (Ik bouw elke release op een Solaris 10 VM).

#!/bin/sh

# use your own variable name instead of txr_shell;
# adjust to taste: search for your favorite shells

if test x$txr_shell = x ; then
  for shell in /bin/bash /usr/bin/bash /usr/xpg4/bin/sh ; do
    if test -x $shell ; then
       txr_shell=$shell
       break
    fi
  done
  if test x$txr_shell = x ; then
    echo "No known POSIX shell found: falling back on /bin/sh, which may not work"
    txr_shell=/bin/sh
  fi
  export txr_shell
  exec $txr_shell $0 ${@+"$@"}
fi

# rest of the script here, executing in upgraded shell 

Het idee hier is dat we een betere shell vinden dan degene waar we onder draaien, en het script opnieuw uitvoeren met die shell. De omgevingsvariabele txr_shell is ingesteld, zodat het opnieuw uitgevoerde script weet dat dit het opnieuw uitgevoerde recursieve exemplaar is.

(In mijn script wordt de txr_shell variabele vervolgens ook gebruikt, voor precies twee doeleinden: ten eerste wordt het afgedrukt als onderdeel van een informatief bericht in de uitvoer van het script. Ten tweede wordt het als de SHELL variabele in de Makefile geïnstalleerd, zodat make zal deze shell ook gebruiken voor het uitvoeren van recepten.)

Op een systeem waar /bin/sh een streepje is, kunt u zien dat de bovenstaande logica /bin/bash zal vinden en het script daarmee opnieuw uitvoeren.

Op een Solaris 10-box zal de /usr/xpg4/bin/sh starten als er geen Bash gevonden wordt.

De proloog is geschreven in een conservatief schaaldialect, met behulp van test voor bestandbestendigheidstests, en de ${@+"$@"} -truc voor het uitbreiden van argumenten met betrekking tot enkele gebroken oude shells (die gewoon "$@" als we in een POSIX-conforme schaal).

2 comments
Charles Duffy 07/31/2017
Je zou de x hackery niet nodig hebben als de juiste citaten in gebruik waren, omdat de situaties die dat idioom verplichtten nu afgekeurde testaanroepingen omringen met -a of -a combinatie van meerdere tests.
Kaz 07/31/2017
@CharlesDuffy inderdaad; de test x$whatever ik daar aan het doen ben lijkt op een ui in de vernis. Als we de gebroken oude shell niet kunnen gebruiken om te citeren, is de laatste ${@+"$@"} -poging zinloos.

zwol 07/31/2017.

Alle varianten van de Bourne shelltaal zijn objectief verschrikkelijk in vergelijking met moderne scripttalen zoals Perl, Python, Ruby, node.js en zelfs (betwistbaar) Tcl. Als je iets zelfs gecompliceerd moet doen, zul je op de lange termijn gelukkiger zijn als je een van bovenstaande gebruiken in plaats van een shellscript.

Het enige echte voordeel dat de shell-taal nog heeft over die nieuwere talen is dat something dat zichzelf /bin/sh noemt gegarandeerd bestaat in alles dat beweert Unix te zijn. Maar dat is misschien niet eens POSIX-compatibel; veel van de oude eigen Unixes bevroor de taal die is geïmplementeerd door /bin/sh en de hulpprogramma's in het standaard PATH prior aan de wijzigingen die worden geëist door Unix95 (ja, Unix95, twintig jaar geleden en het tellen). Er kan een set Unix95 zijn, of zelfs POSIX.1-2001 als je geluk hebt, tools in een map die not op het standaard PATH staat (bijv. /usr/xpg4/bin ) maar ze zijn niet gegarandeerd.

De basis van Perl is more likely aanwezig op een willekeurig geselecteerde Unix-installatie dan Bash is. (Met "de basisprincipes van Perl" bedoel ik /usr/bin/perl bestaat en is een some , mogelijk vrij oude, versie van Perl 5, en als je geluk hebt, is de set modules die met die versie van de tolk is meegeleverd ook beschikbaar.)

daarom:

Als u iets schrijft dat everywhere moet werken dat Unix lijkt te zijn (zoals een "configure" -script), moet u #! /bin/sh Gebruiken #! /bin/sh #! /bin/sh , en je hoeft helemaal geen extensies te gebruiken. Tegenwoordig zou ik in deze omstandigheden POSIX.1-2001-compatibele shell schrijven, maar ik zou bereid zijn om POSIXisms uit te zetten als iemand om ondersteuning voor roestig ijzer vroeg.

Maar als je not iets schrijft dat overal moet werken, dan moet je op het moment dat je in de verleiding komt om enig Bashisme te gebruiken in plaats daarvan stoppen en het hele ding in een betere scripttaal herschrijven. Je toekomstige zelf zal je bedanken.

(Dus wanneer is het gepast om Bash-extensies te gebruiken? Om te bestellen: nooit. Naar de tweede orde: alleen om de Bash-interactieve omgeving uit te breiden - bijvoorbeeld om slimme tabaanvulling en mooie aanwijzingen te bieden.)

Related questions

Hot questions

Language

Popular Tags