#! / bin / sh vs #! / bin / bash för maximal portabilitet

cherouvim 09/03/2017. 3 answers, 2.821 views
linux bash shell sh

Jag brukar arbeta med Ubuntu LTS-servrar som från vad jag förstår symlink /bin/sh till /bin/dash . Många andra distros men symlink /bin/sh till /bin/bash .

Därefter förstår jag att om ett skript använder #!/bin/sh på toppen, kanske det inte går samma sätt på alla servrar?

Finns det en föreslagen praxis på vilket skal att använda för skript när man vill ha maximal portabilitet för dessa skript mellan servrar?

1 Comments
Thorbjørn Ravn Andersen 08/01/2017
Det finns små skillnader mellan de olika skalen. Om portabilitet är det viktigaste för dig, använd sedan #!/bin/sh och använd inte något annat än vad det ursprungliga skalet gav.

3 Answers


Gordon Davisson 08/01/2017.

Det finns ungefär fyra nivåer av bärbarhet för skalskript (vad gäller shebang-linjen):

  1. Mest bärbara: använd en #!/bin/sh shebang och använd only den grundläggande skalsyntaxen som anges i POSIX-standarden . Detta borde fungera på nästan alla POSIX / unix / linux-system. (Tja, förutom Solaris 10 och tidigare som hade det äkta äkta Bourne-skalet, förutse POSIX så icke-kompatibelt, som /bin/sh .)

  2. Näst mest bärbara: använd en #!/bin/bash (eller #!/usr/bin/env bash ) shebang-linje och #!/usr/bin/env bash till bash v3-funktionerna. Detta kommer att fungera på alla system som har bash (på den förväntade platsen).

  3. Tredje mest bärbara: använd en #!/bin/bash (eller #!/usr/bin/env bash ) shebang-linje och använd bash v4-funktioner. Detta kommer att misslyckas på något system som har bash v3 (t.ex. macOS, som måste använda det för licensersättning).

  4. Minst bärbar: använd en #!/bin/sh shebang och använd bash förlängningar till POSIX shell syntaxen. Detta kommer att misslyckas på något system som har något annat än bash för / bin / sh (t.ex. senaste Ubuntu-versioner). Gör aldrig det här; Det är inte bara en kompatibilitetsfråga, det är bara vanligt fel. Tyvärr är det ett fel som många människor gör.

Min rekommendation: Använd de mest konservativa av de första tre som levererar alla skalfunktioner som du behöver för manuset. För max portabilitet, använd alternativ # 1, men enligt min erfarenhet är vissa bash-funktioner (som arrayer) tillräckligt bra för att jag ska gå med # 2.

Det värsta du kan göra är # 4, med fel shebang. Om du inte är säker på vilka funktioner som är basiska POSIX och vilka är bash-extensions, antingen håll dig fast med en bash shebang (dvs alternativ # 2), eller testa skriptet grundligt med ett mycket grundläggande skal (som dash på dina Ubuntu LTS-servrar). Ubuntu wiki har en bra lista över bashisms att se upp för .

Det finns mycket bra information om historien och skillnaderna mellan skalen i Unix & Linux-frågan "Vad betyder det att det är kompatibelt med sh?" och Stackoverflow-frågan "Skillnad mellan sh och bash" .

Också vara medveten om att skalet inte är det enda som skiljer sig från olika system. Om du är van vid Linux, är du van vid GNU-kommandon, som har många icke-standardiserade tillägg som du inte hittar på andra unix-system (t.ex. bsd, macOS). Tyvärr finns det ingen enkel regel här, du behöver bara veta variationens variation för de kommandon du använder.

En av de nästiest kommandona i form av portabilitet är en av de mest grundläggande: echo . Varje gång du använder det med några alternativ (t.ex. echo -n eller echo -e ) eller med några släppningar (backslashes) i strängen som ska skrivas ut, kommer olika versioner att göra olika saker. När som helst du vill skriva ut en sträng utan en linjefod efter den, eller med flykt i strängen, använd printf istället (och lär dig hur det fungerar - det är mer komplicerat än echo är). ps kommandot är också en röra .

En annan generell sak att titta på är nyligen / GNUish-tillägg till kommandoradssyntax: gammalt (standard) kommandot format är att kommandot följs av alternativ (med ett enda streck och varje alternativ är ett enstaka bokstav) följt av kommandot argument. Nyligen (och ofta icke-bärbara) varianter inkluderar långa alternativ (vanligtvis introducerad med -- ), vilket gör att alternativen kan komma efter argument och använder -- att skilja alternativ från argument.

5 comments
12 pabouk 07/30/2017
Det fjärde alternativet är helt enkelt en fel idé. Vänligen använd inte det.
3 Gordon Davisson 07/30/2017
@pabouk Jag håller helt med, så jag redigerade mitt svar för att göra det tydligare.
1 jlliagre 07/30/2017
Din första uttalande är något vilseledande. POSIX-standarden anger inte något om shebang-externt att använda det leder till ospecificerat beteende. Dessutom anger POSIX inte var posix skalet ska vara beläget, bara dess namn ( sh ), så /bin/sh garanteras inte att vara rätt väg. Den mest bärbara är då inte att specificera någon shebang alls eller att anpassa shebang till det använda operativsystemet.
2 Michael Kjörling 07/30/2017
Jag föll i # 4 med ett skript av mig nyligen, och kunde bara inte räkna ut varför det inte fungerade. trots allt fungerade samma kommandon solidt och gjorde precis vad jag ville att dom skulle göra när jag försökte dem direkt i skalet. Så snart jag ändrade #!/bin/sh till #!/bin/bash men skriptet fungerade perfekt. (För mitt försvar, det skriptet hade utvecklats över tiden från en som egentligen bara behövde sh-isms, till en som förlitade sig på bash-liknande beteende.)
2 jlliagre 07/30/2017
@ MichaelKjörling Det (sanna) Bourne-skalet är nästan aldrig buntat med Linux-distributioner och är ändå inte POSIX-kompatibelt. POSIX-standardskalet skapades från ksh beteende, inte Bourne. Vad de flesta Linux-distributioner som följer med FHS gör att /bin/sh är en symbolisk länk till det skal de väljer för att tillhandahålla POSIX-kompatibilitet, vanligtvis bash eller dash .

Kaz 07/31/2017.

I ./configure som förbereder TXR-språket för att bygga skrev jag följande prolog för bättre portabilitet. Skriptet startar upp sig själv även om #!/bin/sh är ett Bourne Shell som inte är POSIX-kompatibelt. (Jag bygger varje release 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 

Tanken här är att vi hittar ett bättre skal än det vi kör under, och kör körningen igen med hjälp av det här skalet. txr_shell miljövariabeln är inställd så att det återexekverade skriptet vet att det är den återexekverade rekursiva förekomsten.

(I mitt skript används txr_shell variabeln också för exakt två ändamål: För det första skrivs den som en del av ett informativt meddelande i skriptets utdata. För det andra är det installerat som SHELL variabeln i Makefile , så att make kommer att använda detta skal också för att utföra recept.)

På ett system där /bin/sh är bindestreck kan du se att ovanstående logik kommer att hitta /bin/bash och utföra skriptet med det igen.

I en Solaris 10-box kommer /usr/xpg4/bin/sh att sparka in om ingen Bash finns.

Prologén är skriven i en konservativ skaldialekt, med hjälp av test för filexistensprov och ${@+"$@"} trick för att expandera argument som åtgärdar några brutna gamla skal (som helt enkelt skulle vara "$@" om vi var i ett POSIX-överensstämmande skal).

2 comments
Charles Duffy 07/31/2017
Man skulle inte behöva x hackery om korrekt citering användes, eftersom situationer som mandat det idiomet omger nu-avkodade testinfordringar med -a eller -o kombinera flera test.
Kaz 07/31/2017
@CharlesDuffy faktiskt; test x$whatever som test x$whatever som jag förefaller där ser ut som en lök i lacken. Om vi ​​inte kan lita på det trasiga gamla skalet att citera, är det slutliga ${@+"$@"} försöket meningslöst.

zwol 07/31/2017.

Alla variationer av Bourne skalspråk är objektivt hemskt i jämförelse med moderna skriptspråk som Perl, Python, Ruby, node.js och till och med (förmodligen) Tcl. Om du måste göra någonting lite komplicerat kommer du att bli lyckligare på sikt om du använder något av ovanstående istället för ett skalskript.

Den enda fördelen som skalsspråket fortfarande har över de nyare språken är att something kallar sig själv /bin/sh är garanterat att det finns något som påstås vara Unix. Det kan dock inte ens vara POSIX-kompatibelt. många av de äldre proprietära Unixesna frös språket som implementerats av /bin/sh och verktygen i standard-PATH prior de förändringar som Unix95 krävde (ja, Unix95, tjugo år sedan och räknar). Det kan finnas en uppsättning Unix95, eller till och med POSIX.1-2001 om du har tur, verktyg i en katalog som not på standard PATH (t.ex. /usr/xpg4/bin ) men de kan inte garanteras att existera.

Grunderna för Perl är dock more likely att vara närvarande i en godtycklig utvald Unix-installation än Bash. (Med "Grunderna i Perl" menar jag /usr/bin/perl och det är some , kanske ganska gammal version av Perl 5, och om du är lycklig är modulen som levereras med den här versionen av tolken också tillgängliga.)

Därför:

Om du skriver något som måste fungera everywhere som påstås vara Unix (t.ex. ett "configure" -skript), måste du använda #! /bin/sh #! /bin/sh , och du behöver inte använda några tillägg alls. Nuförtiden skulle jag skriva POSIX.1-2001-kompatibelt skal under dessa omständigheter, men jag skulle vara beredd att patchera ut POSIXisms om någon frågade om stöd för rostigt järn.

Men om du not skriver något som måste fungera överallt, då är du frestad att använda någon basism alls, du borde stoppa och skriva om hela saken på ett bättre skriptspråk istället. Din framtida själv kommer tacka dig.

(Så när is det lämpligt att använda Bash-tillägg? Första order: aldrig. För andra order: bara för att utöka Bash-interaktiva miljöer - till exempel för att ge smart flikavslutning och fina instruktioner.)

Related questions

Hot questions

Language

Popular Tags