#! / bin / sh vs #! / bin / bash for maksimal portabilitet

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

Jeg jobber vanligvis med Ubuntu LTS-servere som fra hva jeg forstår symlink /bin/sh til /bin/dash . Mange andre distroer om symlink /bin/sh til /bin/bash .

Fra det forstår jeg at hvis et skript bruker #!/bin/sh på toppen, kan det ikke kjøre på samme måte på alle servere?

Er det en foreslått praksis som skal brukes til skript når man vil ha maksimal portabilitet for disse skriptene mellom servere?

1 Comments
Thorbjørn Ravn Andersen 08/01/2017
Det er små forskjeller mellom de forskjellige skjellene. Hvis portabilitet er det viktigste for deg, bruk deretter #!/bin/sh og bruk ikke noe annet enn det originale skallet oppgav.

3 Answers


Gordon Davisson 08/01/2017.

Det er omtrent fire nivåer av bærbarhet for shell-skript (så langt som shebang-linjen gjelder):

  1. Mest bærbare: bruk en #!/bin/sh shebang og bruk only grunnleggende shell-syntaksen som er angitt i POSIX-standarden . Dette bør fungere på stort sett alle POSIX / unix / linux-systemer. (Vel, bortsett fra Solaris 10 og tidligere, som hadde det virkelige eldre Bourne-skallet, forutsetter POSIX så ikke-kompatibelt, som /bin/sh .)

  2. Nest mest bærbare: bruk en #!/bin/bash (eller #!/usr/bin/env bash ) shebang linje, og hold deg til bash v3-funksjoner. Dette vil fungere på ethvert system som har bash (på forventet sted).

  3. Tredje mest bærbare: bruk en #!/bin/bash (eller #!/usr/bin/env bash ) shebang linje, og bruk bash v4 funksjoner. Dette vil mislykkes på ethvert system som har bash v3 (f.eks. MacOS, som må bruke det av lisensieringsgrunner).

  4. Minst bærbar: bruk en #!/bin/sh shebang og bruk bash extensions til POSIX shell syntaks. Dette vil mislykkes på et system som har noe annet enn bash for / bin / sh (for eksempel nyere Ubuntu-versjoner). Gjør aldri dette; det er ikke bare et kompatibilitetsproblem, det er bare feil. Dessverre er det en feil mange mennesker gjør.

Min anbefaling: Bruk de mest konservative av de tre første som leverer alle shell-funksjonene du trenger for skriptet. For maksimal portabilitet, bruk alternativ # 1, men i min erfaring er noen bash-funksjoner (som arrayer) nyttige nok til at jeg skal gå med # 2.

Det verste du kan gjøre er # 4, ved hjelp av feil shebang. Hvis du ikke er sikker på hvilke funksjoner som er grunnleggende POSIX, og som er bash-utvidelser, holder du enten med en bash shebang (dvs. alternativ # 2), eller test skriptet grundig med et veldig grunnleggende skall (som dash på Ubuntu LTS-serverne). Ubuntu wiki har en god liste over bashisms å passe på .

Det er noen veldig gode opplysninger om historien og forskjellene mellom skjell i Unix og Linux spørsmålet "Hva betyr det å være kompatibelt med Sh?" og Stackoverflow-spørsmålet "Forskjell mellom sh og bash" .

Vær også oppmerksom på at skallet ikke er det eneste som skiller seg mellom forskjellige systemer; Hvis du er vant til Linux, er du vant til GNU-kommandoene, som har mange ikke-standardiserte utvidelser, du kanskje ikke finner på andre unix-systemer (f.eks. bsd, macOS). Dessverre er det ingen enkel regel her, du må bare kjenne variasjonsområdet for kommandoene du bruker.

En av de nestiest kommandoene i form av portabilitet er en av de mest grunnleggende: echo . Hver gang du bruker det med noen alternativer (f.eks. echo -n eller echo -e ), eller med noen rømming i strengen som skal skrives ut, vil forskjellige versjoner gjøre forskjellige ting. Når du vil skrive ut en streng uten linjepost etter den, eller med rømming i strengen, bruk printf stedet (og lær hvordan det fungerer - det er mer komplisert enn echo er). ps kommandoen er også et rot .

En annen generell ting å se på er nylig / GNUish-utvidelser til kommandobeslutningssyntax: gammelt (standard) kommandotilstand er at kommandoen etterfølges av alternativer (med et enkelt dash, og hvert alternativ er et enkelt brev), etterfulgt av kommando argumenter. Nylige (og ofte ikke-bærbare) varianter inkluderer lange alternativer (vanligvis introdusert med -- ), slik at alternativene kan komme etter argumenter, og bruker -- å skille mellom alternativer fra argumenter.

5 comments
12 pabouk 07/30/2017
Det fjerde alternativet er rett og slett en feil ide. Vennligst ikke bruk den.
3 Gordon Davisson 07/30/2017
@pabouk Jeg er helt enig, så jeg redigerte mitt svar for å gjøre dette tydeligere.
1 jlliagre 07/30/2017
Din første uttalelse er litt misvisende. POSIX-standarden spesifiserer ikke noe om Shebang utenfor å fortelle å bruke det, fører til uspesifisert oppførsel. Videre spesifiserer POSIX ikke hvor posix skallet skal være plassert, bare dets navn ( sh ), så /bin/sh er ikke garantert å være den rette banen. Den mest bærbare er da ikke å spesifisere noen shebang i det hele tatt, eller å tilpasse shebang til operativsystemet som brukes.
2 Michael Kjörling 07/30/2017
Jeg falt i # 4 med et skript av meg veldig nylig, og bare kunne ikke finne ut hvorfor det ikke fungerte; Tross alt jobbet de samme kommandoene solidt, og gjorde akkurat det jeg ønsket dem å gjøre, da jeg prøvde dem direkte i skallet. Så snart jeg endret #!/bin/sh til #!/bin/bash skjønt, virket skriptet perfekt. (For mitt forsvar hadde dette skriptet utviklet seg over tid fra en som egentlig bare trengte sh-isms, til en som stod på bash-lignende atferd.)
2 jlliagre 07/30/2017
@ MichaelKjörling Det (sanne) Bourne-skallet er nesten aldri samlet med Linux-distribusjoner og er ikke POSIX-kompatibel uansett. POSIX-standardskallet ble opprettet fra ksh oppførsel, ikke Bourne. Hva de fleste Linux-distribusjoner som følger med FHS gjør, er at /bin/sh er en symbolsk lenke til skallet de velger for å gi POSIX-kompatibilitet, vanligvis bash eller dash .

Kaz 07/31/2017.

I ./configure som forbereder TXR-språket for bygging, skrev jeg følgende prolog for bedre portabilitet. Skriptet vil starte opp selv om #!/bin/sh er et Bourne Shell som ikke er POSIX-kompatibelt. (Jeg bygger hver utgivelse på en 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 

Ideen her er at vi finner et bedre skall enn det vi kjører under, og utfør skriptet på nytt ved hjelp av det skallet. txr_shell miljøvariabelen er satt, slik at det txr_shell skriptet vet at det er den gjenutførte rekursive forekomsten.

(I skriptet blir txr_shell variabelen også brukt for nøyaktig to formål: For det første skrives den ut som en del av en informativ melding i utskriften av skriptet. For det andre er det installert som SHELL variabelen i Makefile , slik at make vil bruke dette skallet også for å utføre oppskrifter.)

På et system hvor /bin/sh er dash, kan du se at logikken ovenfor vil finne /bin/bash og utføre skriptet med det.

På en Solaris 10-boks vil /usr/xpg4/bin/sh sparke inn hvis ingen Bash er funnet.

Prologoen er skrevet i en konservativ skalldialekt, ved hjelp av test for fileksistensforsøk, og ${@+"$@"} -tricket for å utvide argumenter som passer til noen ødelagte gamle skaller (som bare ville være "$@" hvis vi var i et POSIX-samsvarende skall).

2 comments
Charles Duffy 07/31/2017
Man ville ikke trenge x hackeryen hvis riktig sitering ble brukt, siden situasjonene som innebar at idiomet omgir nå-avviklet testinnkallinger med -a eller -o kombinere flere tester.
Kaz 07/31/2017
@CharlesDuffy Faktisk; test x$whatever jeg begår det ser ut som en løk i lakken. Hvis vi ikke kan stole på det ødelagte gamle skallet som skal sitere, er det endelige ${@+"$@"} forsøket meningsløst.

zwol 07/31/2017.

Alle variasjoner av Bourne skallet språket er objektivt forferdelig i forhold til moderne skriptspråk som Perl, Python, Ruby, node.js og til og med (uten tvil) Tcl. Hvis du må gjøre noe enda litt komplisert, vil du bli lykkeligere i det lange løp hvis du bruker en av de ovennevnte i stedet for et skallskript.

Den eneste fordelen som skallsspråket fortsatt har over de nyere språkene er at something kaller seg selv /bin/sh er garantert å eksistere på alt som hevder å være Unix. Det kan imidlertid ikke engang være POSIX-kompatibel; mange av arvenes proprietære Unixes frøs språket implementert av /bin/sh og verktøyene i standard PATH prior endringene som Unix95 krever (ja, Unix95, tjue år siden og telle). Det kan være et sett med Unix95, eller til og med POSIX.1-2001 hvis du er heldig, verktøy i en katalog som not på standard PATH (f.eks /usr/xpg4/bin ), men de er ikke garantert å eksistere.

Grunnleggende om Perl er imidlertid more likely å være til stede på en vilkårlig valgt Unix-installasjon enn Bash er. (Ved "Grunnleggende om Perl" mener jeg /usr/bin/perl og er some , kanskje ganske gammel, versjon av Perl 5, og hvis du er heldig, er modulen som leveres med den tolkversjonen også tilgjengelig.)

Derfor:

Hvis du skriver noe som må fungere everywhere som utgjør Unix (for eksempel et "configure" -skript), må du bruke #! /bin/sh #! /bin/sh , og du trenger ikke å bruke noen utvidelser overhodet. I dag vil jeg skrive POSIX.1-2001-kompatibelt skall under denne omstendigheten, men jeg ville være forberedt på å lappe ut POSIXisms hvis noen ba om støtte til rustfritt jern.

Men hvis du not skriver noe som må fungere overalt, så i det øyeblikket du er fristet til å bruke noen Bashism i det hele tatt, bør du stoppe og omskrive hele greia på et bedre skriptspråk i stedet. Din fremtidige selv vil takke deg.

(Så når is det hensiktsmessig å bruke Bash-utvidelser? Til førstegangsbestilling: Aldri. For andre ordre: Bare for å utvide Bash interaktive miljø - for eksempel å gi smart tabulasjon og fulle oppfordringer.)

Related questions

Hot questions

Language

Popular Tags