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?


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 .


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 ]).


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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.