Para loop com alfabeto

12

Funciona perfeitamente no OSX

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

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

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

Não consigo resolver o problema. Alguma sugestão?

denski
fonte
2
Isso funciona no Ubuntu.
precisa saber é o seguinte
Não consigo fazê-lo funcionar no 16.04 bash 4.3 como um script. Mas funciona se eu copiar para o terminal.
denski

Respostas:

25

Presumivelmente, você está executando o script como:

sh ForLoopAlphabetTest.sh

No Ubuntu, shestá vinculado a dash; como dashnão tem conceito de matrizes, você está recebendo o erro de sintaxe (.

O script funciona perfeitamente bash, por isso seria bom se você o estivesse executando como bashargumento:

bash ForLoopAlphabetTest.sh

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

/path/to/ForLoopAlphabetTest.sh

ou do diretório do script:

./ForLoopAlphabetTest.sh

Observe também que, seu script contém expansão entre chaves {a..z}e forconstrução no estilo C : as for (( ... ))quais também não são suportadas dash; portanto, se seu objetivo é portabilidade, você deve considerar shapenas as sintaxes do POSIX .

heemail
fonte
Obrigado. Existe uma maneira de contornar a falta de conceito de array de dash?
denski
3
@ denski Se você quiser escrever scripts portáteis que possam ser executados /bin/shem qualquer sistema operacional parecido com o Unix, não poderá usar matrizes. O Bash (e alguns outros shells) os adicionaram porque são muito convenientes e nem sempre podem ser facilmente substituídos por um código mais portátil. No entanto, para o seu script em particular, você pode fazê-lo sem problemas e sem usar nenhum recurso específico do bash. Você está interessado em como fazer isso?
Eliah Kagan
Se você tiver alguma sugestão de leitura, seria útil. Obrigado.
denski
1
Publiquei uma resposta que inclui alguns links e exemplos. No meu comentário anterior, mencionei que você usava matrizes e estilo C para loops, mas não mencionou o uso de expansão de chaves. Minha resposta aborda como passar sem os três. Note que este resposta (ou seja, heemayl, não minha) é a principal solução para o seu problema; o meu enfoca como você pode reescrever seu script se não puder confiar em recursos específicos do bash.
Eliah Kagan 18/01/19
@heemayl Para registro, eu queria acrescentar que você estava correto ao assumir que eu estava executando scripts com sh
denski
10

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 em bashvez de sh. Seu linha de hashbang na parte superior ( #!/bin/bash) especifica, bashmas só é efetiva se você executar o script, como o heemayl explicou . Se você passar o nome do script para sh, shnão será chamado automaticamente bash, mas simplesmente executará o script. Isso ocorre porque, quando o script está em execução, a linha hashbang não tem efeito .

Sua outra alternativa, se você precisar escrever scripts totalmente portáteis que não dependem dos recursos do Bash, é alterá-lo para que funcione sem eles. Os recursos do Bash que você usa são:

  • Uma matriz . Isso ocorreu primeiro, e foi isso que produziu o erro quando você tentou executar seu script com o shell Dash . A expressão entre parênteses ( {a..z} ), à qual você atribui chars, cria uma matriz e ${chars[i]}, que aparece no seu loop, é indexada nela.
  • Expanda a cinta. No Bash, e também em muitas outras conchas, {a..z}é expandido para 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. No entanto, esse não é um recurso universal (ou padronizado) dos shells no estilo Bourne, e o Dash não o suporta.
  • O C-estilo alternativo forsintaxe -loop . Embora baseado na expansão aritmética , que não é específica do Bash (embora alguns shells muito antigos e não compatíveis com POSIX também não o tenham), o forloop do estilo C é um Bash-ism e não é amplamente portátil para outras conchas.

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, convém 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.

No entanto, você pode substituí-los por construções portáteis, se quiser. A matriz e o forloop em estilo C são fáceis de substituir; gerar o intervalo de letras sem expansão entre chaves (e sem codificá-las em seu script) é a parte um pouco 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

É portátil para a maioria dos sistemas Unix e não depende de qual shell Bourne você usa. No entanto, alguns sistemas tipo Unix não são seqinstalados por padrão (eles tendem a usar jot, o que não é instalado por padrão na maioria dos sistemas GNU / Linux). Você pode usar um loop com exprsubstituição aritmética ou 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 loop whilecom o [comando para continuar fazendo loop apenas quando $iestiver dentro do alcance.


Em vez de imprimir o alfabeto inteiro, seu script define uma variável ne imprime as primeiras $nletras 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 seq:

#!/bin/sh

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

Ajustar o valor das nalterações quantas letras são impressas, como no seu script.

Aqui está uma versão que não requer seq:

#!/bin/sh

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

Existe $stopum maior que o código de caractere da última letra que deve ser impressa; portanto, uso -lt(menor que) do que -le(menor ou igual) ao [comando. (Também teria funcionado para criar stop=$((i + n - 1))e usar [ $i -le $stop ]).

Eliah Kagan
fonte
1
Esta é uma resposta fenomenalmente detalhada do que você para a educação. Eu sou muito iniciante, então minha maneira de escrever scripts está reunindo elementos de trabalho encontrados na internet até que funcione. Não tenho dúvidas de que existem 1) maneiras melhores e 2) mais simples de fazer as coisas que estou criando com scripts, e o que foi mencionado acima ajuda bastante nisso.
denski
Relacionado: stackoverflow.com/questions/169511/…
Ciro Santilli escreveu: