Para loop com o alfabeto

11

Isso funciona perfeitamente no OSX

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

Mas quando eu executo no Ubuntu, recebo o seguinte erro.

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

Eu não consigo resolver o problema. Alguma sugestão?

    
por denski 18.01.2017 / 13:22

2 respostas

25

Presumivelmente, você está executando o script como:

sh ForLoopAlphabetTest.sh

No Ubuntu, sh é vinculado a dash ; como dash não tem nenhum conceito de arrays, você está recebendo o erro de sintaxe para ( .

O script funciona perfeitamente em bash , então seria bom se você o estivesse executando como o argumento bash :

bash ForLoopAlphabetTest.sh

Agora, você tem o bash shebang no script, então você pode tornar o script executável ( chmod u+x ForLoopAlphabetTest.sh ) e executá-lo como:

/path/to/ForLoopAlphabetTest.sh

ou no diretório do script:

./ForLoopAlphabetTest.sh

Observe também que seu script contém a expansão de concha {a..z} e o estilo C for construct: for (( ... )) , que também não são suportados por dash ; por isso, se o seu objetivo for a portabilidade, você deve olhar apenas as sintaxes POSIX sh .

    
por heemayl 18.01.2017 / 13:31
8

Seu script usa três recursos do shell Bash que não são fornecidos por todos os shells no estilo Bourne. Como o heemayl diz , você pode simplesmente executar esse script com bash em vez de sh . Sua linha hashbang no topo ( #!/bin/bash ) especifica bash , mas só é efetiva se você > execute o script, como heemayl explicou . Se você passar o nome do script para sh , sh não chamará automaticamente bash , mas simplesmente executará o script. Isto porque uma vez que seu script está realmente rodando, a linha hashbang não tem efeito .

Sua outra alternativa, se você precisar escrever scripts totalmente portáteis que não dependam nos recursos do Bash, é mudar seu script para que ele funcione sem eles. Os recursos de Bash que você usa são:

O Bash está amplamente disponível, especialmente em sistemas GNU / Linux como o Ubuntu, e (como você viu) também está disponível no macOS e em muitos outros sistemas. Considerando o quanto você está usando recursos específicos do Bash, você pode apenas querer usá-los, e simplesmente certifique-se de usar o Bash (ou algum outro shell que suporte os recursos que você está usando) ao executar seus scripts. / p>

No entanto, você pode substituí-los por construções portáteis, se quiser. A matriz e o loop for do estilo C são fáceis de substituir; Gerar o intervalo de letras sem expansão de chaves (e sem codificá-las em seu script) é a única parte complicada.

Primeiro, aqui está um script que imprime todas as letras latinas em minúsculas:

#!/bin/sh

for i in $(seq 97 122); do
    printf "\$(printf %o $i)\n"
done

Isso é portátil para os sistemas mais Unix e não depende de qual shell estilo Bourne você usa. No entanto, alguns sistemas semelhantes a Unix não têm ' instalado por padrão (eles tendem a usar seq , que não é instalado por padrão na maioria dos sistemas GNU / Linux). Você pode usar um loop com jot ou substituição aritmética para aumentar ainda mais a portabilidade, se precisar:

#!/bin/sh

i=97
while [ $i -le 122 ]; do
    printf "\$(printf %o $i)\n"
    i=$((i + 1))
done

Isso usa um expr -loop com o comando while para continuar o loop somente quando [ estiver no intervalo.

Em vez de imprimir todo o alfabeto, o script define uma variável $i e imprime as primeiras letras n minúsculas. Aqui está uma versão do seu script que não depende de recursos específicos do Bash e funciona no Dash, mas requer $n :

#!/bin/sh

n=3 start=97
for i in $(seq $start $((start + n - 1))); do
    printf "\$(printf %o $i)\n"
done

Ajustar o valor de seq altera quantas letras são impressas, como no seu script.

Aqui está uma versão que não exige n :

#!/bin/sh

n=3 i=97 stop=$((i + n))
while [ $i -lt $stop ]; do
    printf "\$(printf %o $i)\n"
    i=$((i + 1))
done

Lá, seq é um maior do que o código de caractere da última letra que deveria ser impressa, então eu uso $stop (menor que) em vez de -lt (menor que ou igual) com o comando -le . (Também teria trabalhado para fazer [ e usar stop=$((i + n - 1)) ).

    
por Eliah Kagan 18.01.2017 / 17:02