GNU/Linux >> Linux Esercitazione >  >> Linux

#!/bin/sh vs #!/bin/bash per la massima portabilità

Soluzione 1:

Ci sono approssimativamente quattro livelli di portabilità per gli script di shell (per quanto riguarda la linea shebang):

  1. Il più portatile:usa un #!/bin/sh shebang e usa solo la sintassi di base della shell specificata nello standard POSIX. Questo dovrebbe funzionare praticamente su qualsiasi sistema POSIX/unix/linux. (Beh, tranne Solaris 10 e versioni precedenti che avevano la vera shell Bourne legacy, precedente a POSIX quindi non conforme, come /bin/sh .)

  2. Secondo più portatile:usa un #!/bin/bash (o #!/usr/bin/env bash ) shebang e attenersi alle funzionalità di bash v3. Funzionerà su qualsiasi sistema che abbia bash (nella posizione prevista).

  3. Terzo più portatile:usa un #!/bin/bash (o #!/usr/bin/env bash ) shebang e utilizza le funzionalità di bash v4. Questo fallirà su qualsiasi sistema che abbia bash v3 (ad esempio macOS, che deve usarlo per motivi di licenza).

  4. Meno portabile:usa un #!/bin/sh shebang e usa le estensioni bash per la sintassi della shell POSIX. Ciò fallirà su qualsiasi sistema che abbia qualcosa di diverso da bash per /bin/sh (come le recenti versioni di Ubuntu). Non farlo mai; non è solo un problema di compatibilità, è semplicemente sbagliato. Sfortunatamente, è un errore che fanno molte persone.

Il mio consiglio:usa il più conservativo dei primi tre che fornisce tutte le funzionalità della shell di cui hai bisogno per lo script. Per la massima portabilità, usa l'opzione n. 1, ma nella mia esperienza alcune funzionalità di bash (come gli array) sono abbastanza utili da scegliere la n. 2.

La cosa peggiore che puoi fare è #4, usare lo shebang sbagliato. Se non sei sicuro di quali funzionalità siano POSIX di base e quali siano estensioni bash, attenersi a un bash shebang (ovvero l'opzione n. 2) o testare a fondo lo script con una shell molto semplice (come trattino sui server Ubuntu LTS). Il wiki di Ubuntu ha una buona lista di bashismi a cui prestare attenzione.

Ci sono alcune ottime informazioni sulla storia e le differenze tra le shell nella domanda Unix e Linux "Cosa significa essere compatibili con sh?" e la domanda StackOverflow "Differenza tra sh e bash".

Inoltre, tieni presente che la shell non è l'unica cosa che differisce tra i diversi sistemi; se sei abituato a Linux, sei abituato ai comandi GNU, che hanno molte estensioni non standard che potresti non trovare su altri sistemi Unix (ad esempio bsd, macOS). Sfortunatamente, non esiste una regola semplice qui, devi solo conoscere l'intervallo di variazione per i comandi che stai utilizzando.

Uno dei comandi peggiori in termini di portabilità è uno dei più elementari:echo . Ogni volta che lo usi con qualsiasi opzione (ad es. echo -n o echo -e ), o con eventuali escape (barre rovesciate) nella stringa da stampare, versioni diverse faranno cose diverse. Ogni volta che vuoi stampare una stringa senza un avanzamento riga dopo di essa, o con escape nella stringa, usa printf invece (e scopri come funziona -- è più complicato di echo è). Il ps anche il comando è un casino.

Un'altra cosa generale a cui prestare attenzione sono le estensioni recenti/GNUish alla sintassi delle opzioni di comando:il vecchio formato di comando (standard) è che il comando è seguito da opzioni (con un singolo trattino e ogni opzione è una singola lettera), seguito da argomenti del comando. Varianti recenti (e spesso non trasferibili) includono opzioni lunghe (di solito introdotte con -- ), consentendo alle opzioni di seguire gli argomenti e usando -- per separare le opzioni dagli argomenti.

Soluzione 2:

Nel ./configure script che prepara il linguaggio TXR per la costruzione, ho scritto il seguente prologo per una migliore portabilità. Lo script si avvierà da solo anche se #!/bin/sh è una vecchia Bourne Shell non conforme a POSIX (creo ogni versione su una VM Solaris 10).

#!/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 ${@+"[email protected]"}
fi

# rest of the script here, executing in upgraded shell

L'idea qui è che troviamo una shell migliore di quella in cui stiamo eseguendo e rieseguiamo lo script usando quella shell. Il txr_shell variabile di ambiente è impostata, in modo che lo script rieseguito sappia che è l'istanza ricorsiva rieseguita.

(Nel mio script, il file txr_shell variabile viene anche successivamente utilizzata, esattamente per due scopi:in primo luogo viene stampata come parte di un messaggio informativo nell'output dello script. In secondo luogo, viene installato come SHELL variabile nel Makefile , in modo che make userà questa shell anche per eseguire ricette.)

Su un sistema in cui /bin/sh è un trattino, puoi vedere che la logica precedente troverà /bin/bash e rieseguire lo script con quello.

Su una scatola Solaris 10, il /usr/xpg4/bin/sh entrerà in funzione se non viene trovata alcuna Bash.

Il prologo è scritto in un dialetto shell conservatore, usando test per i test di esistenza dei file e il ${@+"[email protected]"} trucco per espandere gli argomenti che soddisfano alcuni vecchi gusci rotti (che sarebbe semplicemente "[email protected]" se fossimo in una shell conforme a POSIX).

Soluzione 3:

Tutte le variazioni del linguaggio della shell Bourne sono oggettivamente terribili rispetto ai moderni linguaggi di scripting come Perl, Python, Ruby, node.js e persino (probabilmente) Tcl. Se devi fare qualcosa anche un po' complicato, alla lunga sarai più felice se usi uno dei precedenti invece di uno script di shell.

L'unico e unico vantaggio che il linguaggio shell ha ancora rispetto a quei nuovi linguaggi è quel qualcosa chiamandosi /bin/sh è garantito per esistere su tutto ciò che pretende di essere Unix. Tuttavia, quel qualcosa potrebbe non essere nemmeno conforme a POSIX; molti degli Unix proprietari legacy bloccavano il linguaggio implementato da /bin/sh e le utilità nel PATH predefinito prior ai cambiamenti richiesti da Unix95 (sì, Unix95, vent'anni fa e oltre). Potrebbe esserci un set di strumenti Unix95, o anche POSIX.1-2001 se sei fortunato, in una directory non sul PATH predefinito (ad es. /usr/xpg4/bin ) ma non è garantito che esistano.

Tuttavia, le basi di Perl sono più probabili essere presente su un'installazione Unix selezionata arbitrariamente rispetto a Bash. (Con "le basi di Perl" intendo /usr/bin/perl esiste ed è qualche , forse piuttosto vecchia, versione di Perl 5, e se sei fortunato sono disponibili anche i set di moduli forniti con quella versione dell'interprete.)

Pertanto:

Se stai scrivendo qualcosa che deve funzionare ovunque che pretende di essere Unix (come uno script "configure"), devi usare #! /bin/sh e non è necessario utilizzare alcuna estensione. Al giorno d'oggi scriverei una shell conforme a POSIX.1-2001 in questa circostanza, ma sarei pronto a correggere i POSIXismi se qualcuno chiedesse supporto per il ferro arrugginito.

Ma se non lo sei scrivere qualcosa che deve funzionare ovunque, quindi nel momento in cui sei tentato di usare qualsiasi bashismo, dovresti fermarti e riscrivere l'intera cosa in un linguaggio di scripting migliore. Il tuo io futuro ti ringrazierà.

(Quindi, quando è è opportuno utilizzare le estensioni Bash? Al primo ordine:mai. Al secondo ordine:solo per estendere l'ambiente interattivo di Bash, ad es. per fornire il completamento intelligente delle schede e suggerimenti fantasiosi.)


Linux
  1. /usr/bin Vs /usr/local/bin Su Linux?

  2. Qual è la differenza tra /sbin/nologin e /bin/false?

  3. Qual è il motivo per cui uno Shebang punta a /bin/sh piuttosto che a /bin/bash?

  4. Linux – Unire /usr/bin e /usr/sbin in /bin (gnu/linux)?

  5. nozioni di base su grep

Perché /bin/sh punta a /bin/dash e non a /bin/bash??

Cosa c'è di diverso tra /bin/false e /sbin/nologin come shell dell'utente nologin

Qual è la differenza tra #!/usr/bin/env bash e #!/usr/bin/bash?

autorizzazione negata per il compositore in /usr/local/bin/

Differenza tra /bin e /usr/bin

chroot fallisce - impossibile eseguire il comando `/bin/bash':File o directory non presenti