Per loop con Alphabet


12

Funziona perfettamente su OSX

#!/bin/bash
chars=( {a..z} )
n=3
for ((i=0; i<n; i++))
do
  echo "${chars[i]}"
done

Ma quando lo eseguo su Ubuntu, ottengo il seguente errore.

ForLoopAlphabetTest.sh: 2: ForLoopAlphabetTest.sh: Syntax error: "(" unexpected

Non riesco a risolvere il problema. Eventuali suggerimenti?


2
Questo funziona su Ubuntu.
Pilota 6

Non riesco a farlo funzionare su 16.04 bash 4.3 come script. Ma funziona se lo copio nel terminale.
denski,

Risposte:


25

Presumibilmente, stai eseguendo lo script come:

sh ForLoopAlphabetTest.sh

In Ubuntu, shè collegato a dash; poiché dashnon ha alcun concetto di array, si ottiene l'errore di sintassi per (.

Lo script funziona perfettamente bash, quindi andrebbe bene se lo stavi eseguendo come bashargomento:

bash ForLoopAlphabetTest.sh

Ora, hai lo bashshebang nello script, quindi puoi rendere lo script eseguibile ( chmod u+x ForLoopAlphabetTest.sh) ed eseguirlo come:

/path/to/ForLoopAlphabetTest.sh

o dalla directory dello script:

./ForLoopAlphabetTest.sh

Inoltre, lo script contiene l'espansione di parentesi graffe {a..z}e il forcostrutto in stile C : for (( ... ))che non sono supportati da dash; quindi se il tuo obiettivo è la portabilità, dovresti esaminare shsolo le sintassi POSIX .


Grazie. C'è un modo per aggirare la mancanza di un trattino del concetto di array?
denski,

3
@denski Se si desidera scrivere script portatili che possono essere eseguiti /bin/shsu qualsiasi sistema operativo simile a Unix, non sarà possibile utilizzare le matrici. Bash (e alcune altre shell) li hanno aggiunti perché sono molto comodi e non possono sempre essere facilmente sostituiti con più codice portatile. Tuttavia, in particolare per il tuo script, puoi farlo senza problemi e senza utilizzare alcuna funzionalità specifica di bash. Sei interessato a come farlo?
Eliah Kagan,

Se hai qualche suggerimento da leggere sarebbe utile. Grazie.
denski,

1
@denski Ho pubblicato una risposta che include alcuni link ed esempi. Nel mio precedente commento qui, avevo menzionato che hai usato array e C-style per i loop ma non hai menzionato il tuo uso dell'espansione del controvento. La mia risposta copre come fare a meno di tutti e tre. Nota che questa risposta (cioè, heemayl, non la mia) è la soluzione principale al tuo problema; il mio si concentra su come potresti riscrivere il tuo script se non potessi fare affidamento su funzionalità specifiche di bash.
Eliah Kagan,

@heemayl Per la cronaca, volevo aggiungere che avevi ragione nel presupposto che stavo eseguendo degli script consh
denski

10

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 bashinvece di sh. La tua riga hashbang nella parte superiore ( #!/bin/bash) specifica bashma è efficace solo se esegui lo script, come ha spiegato heemayl . Se passi il nome dello script a sh, shnon 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 fordi 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 forloop 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 forloop 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

È 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 seqinstallati per impostazione predefinita (tendono jotinvece a utilizzare , che non è installato per impostazione predefinita sulla maggior parte dei sistemi GNU / Linux). È possibile utilizzare un ciclo con expro 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 ne stampa le prime $nlettere 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 nmodifiche 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ì, $stopc'è 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 ]).


1
Questa è una risposta fenomenale dettagliata di te per l'educazione. Sono molto alle prime armi, quindi il mio modo di scrivere script sta mettendo insieme gli elementi di lavoro trovati su Internet fino a quando non funziona. Non ho dubbi sul fatto che ci siano 1) migliori e 2) modi più semplici di fare le cose che sto creando con gli script e quanto sopra può aiutare molto.
denski

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.