Lo script utilizza tre funzionalità della shell Bash che non sono fornite da tutte le shell in stile Bourne. Come dice heemayl , puoi semplicemente eseguire quello script con bash
invece di sh
. La tua riga hashbang nella parte superiore ( #!/bin/bash
) specifica bash
ma è efficace solo se esegui lo script, come ha spiegato heemayl . Se passi il nome dello script a sh
, sh
non chiamerà automaticamente bash
, ma eseguirà semplicemente lo script. Questo perché una volta che lo script è effettivamente in esecuzione, la riga hashbang non ha alcun effetto .
Un'altra alternativa, se è necessario scrivere script completamente portatili che non dipendono dalle funzionalità di Bash, è modificare lo script in modo che funzioni senza di essi. Le funzionalità di Bash che usi sono:
- Un array . Questo è venuto prima, quindi questo è ciò che ha prodotto l'errore quando si è tentato di eseguire lo script con la shell Dash . L'espressione tra parentesi
( {a..z} )
, a cui si assegna chars
, crea una matrice e ${chars[i]}
, che appare nel ciclo, si indicizza in essa.
- Espansione del rinforzo. In Bash, e anche in molte altre shell,
{a..z}
viene espanso a b c d e f g h i j k l m n o p q r s t u v w x y z
. Tuttavia, questa non è una funzionalità universale (o standardizzata) delle shell in stile Bourne e Dash non la supporta.
- Il C-stile alternativo
for
di sintassi -loop . Sebbene basato sull'espansione aritmetica , che non è di per sé specifica di Bash (anche se alcune shell molto vecchie, non conformi a POSIX non lo hanno,), il for
loop in stile C è un Bash-ism e non è ampiamente portabile altre conchiglie.
Bash è ampiamente disponibile, specialmente su sistemi GNU / Linux come Ubuntu e (come hai visto) è disponibile anche su macOS e molti altri sistemi. Considerando quanto stai usando funzionalità specifiche di Bash, potresti semplicemente volerle usare e assicurarti semplicemente che stai usando Bash (o qualche altra shell che supporti le funzionalità che stai usando) quando esegui i tuoi script.
Tuttavia, se lo desideri, puoi sostituirli con costrutti portatili. L'array e il for
loop in stile C sono facili da sostituire; generare l'intervallo di lettere senza espansione di parentesi graffe (e senza codificarle nel tuo script) è l'unica parte che è un po 'complicata.
Innanzitutto, ecco una sceneggiatura che stampa tutte le lettere latine minuscole:
#!/bin/sh
for i in $(seq 97 122); do
printf "\\$(printf %o $i)\n"
done
- Il
seq
comando genera sequenze numeriche. $(
)
esegue la sostituzione dei comandi , quindi $(seq 97 122)
viene sostituito con l'output di seq 97 122
. Questi sono i codici dei caratteri per a
through z
.
- Il potente
printf
comando può trasformare i codici carattere in lettere (ad esempio, printf '\141'
stampe a
, seguite da una nuova riga ), ma i codici devono essere in ottale , mentre l' seq
output è solo in decimale . Quindi ho usato printf
due volte: l'interno printf %o $i
converte i numeri decimali (forniti da seq
) in ottale e viene sostituito nel printf
comando esterno . (Anche se è anche possibile utilizzare esadecimali , non è più semplice e sembra essere meno portatile .)
printf
interpreta \
seguito da un numero ottale come carattere con quel codice e \n
come una nuova riga. Ma la shell usa anche \
come personaggio di fuga. Un \
davanti $
impedirà $
da provocare un'espansione a verificarsi (in questo caso, il comando di sostituzione ), ma non voglio impedire che, così ho sfuggito con un altro \
; questo è il motivo \\
. Non è necessario eseguire l'escaping del secondo \
prima n
perché, a differenza \$
, \n
non ha un significato speciale per la shell in una stringa tra virgolette doppie.
- Per ulteriori informazioni sull'uso delle virgolette doppie e della barra rovesciata nella programmazione della shell, vedere la sezione sulle quotazioni nello standard internazionale . Vedi anche 3.1.2 Citazioni nel Manuale di riferimento di Bash , in particolare 3.1.2.1 Escape Character e 3.1.2.3 Double Quotes . (Ecco l'intera sezione , nel contesto.) Nota che le virgolette singole (
'
) sono anche una parte importante della sintassi delle citazioni di shell, non mi è capitato di averle usate in quello script.
È portatile per la maggior parte dei sistemi simili a Unix e non dipende dalla shell in stile Bourne che usi. Tuttavia, alcuni sistemi simili a Unix non sono seq
installati per impostazione predefinita (tendono jot
invece a utilizzare , che non è installato per impostazione predefinita sulla maggior parte dei sistemi GNU / Linux). È possibile utilizzare un ciclo con expr
o sostituzione aritmetica per aumentare ulteriormente la portabilità, se è necessario:
#!/bin/sh
i=97
while [ $i -le 122 ]; do
printf "\\$(printf %o $i)\n"
i=$((i + 1))
done
Che utilizza un while
-loop con il [
comando per continuare il looping solo quando $i
è nell'intervallo.
Invece di stampare l'intero alfabeto, lo script definisce una variabile n
e stampa le prime $n
lettere minuscole. Ecco una versione del tuo script che non si basa su funzionalità specifiche di Bash e funziona su Dash, ma richiede seq
:
#!/bin/sh
n=3 start=97
for i in $(seq $start $((start + n - 1))); do
printf "\\$(printf %o $i)\n"
done
Regolando il valore delle n
modifiche quante lettere vengono stampate, come nella tua sceneggiatura.
Ecco una versione che non richiede seq
:
#!/bin/sh
n=3 i=97 stop=$((i + n))
while [ $i -lt $stop ]; do
printf "\\$(printf %o $i)\n"
i=$((i + 1))
done
Lì, $stop
c'è uno più alto del codice carattere dell'ultima lettera che dovrebbe essere stampato, quindi io uso -lt
(minore di) anziché -le
(minore di o uguale) con il [
comando. (Avrebbe anche funzionato per creare stop=$((i + n - 1))
e utilizzare [ $i -le $stop ]
).