Para bucle con alfabeto

12

Esto funciona perfectamente en OSX

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

Pero cuando lo ejecuto en Ubuntu, aparece el siguiente error.

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

Parece que no puedo resolver el problema. ¿Alguna sugerencia?

denski
fuente
2
Esto funciona en Ubuntu.
Pilot6 el
No puedo hacer que funcione en 16.04 bash 4.3 como script. Pero funciona si lo copio en la terminal.
denski

Respuestas:

25

Presumiblemente, está ejecutando el script como:

sh ForLoopAlphabetTest.sh

En Ubuntu, shestá vinculado a dash; como dashno tiene concepto de matrices, está obteniendo el error de sintaxis para (.

El script funciona perfectamente bash, por lo que estaría bien si lo estuviera ejecutando como bashargumento:

bash ForLoopAlphabetTest.sh

Ahora, tiene el bashshebang en el script, por lo que puede hacer que el script sea ejecutable ( chmod u+x ForLoopAlphabetTest.sh) y ejecutarlo como:

/path/to/ForLoopAlphabetTest.sh

o desde el directorio del script:

./ForLoopAlphabetTest.sh

También tenga en cuenta que su script contiene expansión de llaves {a..z}y forconstrucción de estilo C : for (( ... ))que tampoco son compatibles con dash; así que si su objetivo es la portabilidad, debe mirar shsolo las sintaxis POSIX .

heemayl
fuente
Gracias. ¿Hay alguna forma de eludir la falta de concepto del tablero de la matriz?
denski
3
@denski Si desea escribir scripts portátiles que puedan ejecutarse /bin/shen cualquier sistema operativo tipo Unix, entonces no podrá usar matrices. Bash (y algunos otros shells) los han agregado porque son muy convenientes y no siempre se pueden reemplazar fácilmente con un código más portátil. Sin embargo, para su script en particular, puede hacerlo sin problemas y sin usar ninguna característica específica de bash. ¿Estás interesado en cómo hacer eso?
Eliah Kagan
Si ha sugerido alguna lectura, sería útil. Gracias.
denski
1
@denski He publicado una respuesta que incluye algunos enlaces y ejemplos. En mi comentario anterior aquí, mencioné que usaste matrices y estilo C para bucles, pero no mencioné tu uso de la expansión de llaves. Mi respuesta cubre cómo prescindir de los tres. Tenga en cuenta que esta respuesta (es decir, heemayl's, no la mía) es la solución principal a su problema; el mío se centra en cómo podría reescribir su script si no pudiera confiar en las funciones específicas de bash.
Eliah Kagan
@heemayl Para el registro, quería agregar que estaba en lo correcto al suponer que estaba ejecutando scripts consh
denski
10

Su script utiliza tres características del shell Bash que no son provistas por todos los shells de estilo Bourne. Como dice heemayl , simplemente puede ejecutar ese script con en bashlugar de sh. Su línea hashbang en la parte superior ( #!/bin/bash) especifica bashpero solo es efectiva si ejecuta el script, como explicó heemayl . Si pasa el nombre del script a sh, shno llamará automáticamente bash, sino que simplemente ejecutará el script. Esto se debe a que una vez que su script se está ejecutando, la línea hashbang no tiene ningún efecto .

Su otra alternativa, si necesita escribir scripts totalmente portátiles que no dependan de las funciones de Bash, es cambiar su script para que funcione sin ellos. Las características de Bash que usa son:

Bash está ampliamente disponible, especialmente en sistemas GNU / Linux como Ubuntu, y (como ha visto) también está disponible en macOS y muchos otros sistemas. Teniendo en cuenta cuánto está usando las funciones específicas de Bash, es posible que solo desee usarlas, y simplemente asegúrese de estar usando Bash (o algún otro shell que admita las funciones que está usando) cuando ejecuta sus scripts.

Sin embargo, puede reemplazarlos con construcciones portátiles si lo desea. La matriz y el estilo Cfor bucle de son fáciles de reemplazar; Generar el rango de letras sin expansión de llaves (y sin codificarlas en su script) es la parte que es un poco complicada.


Primero, aquí hay un script que imprime todas las letras latinas en minúsculas:

#!/bin/sh

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

Esto es portátil para la mayoría de los sistemas tipo Unix y no depende de qué shell Bourne use. Sin embargo, algunos sistemas similares a Unix no se han seqinstalado por defecto (tienden a usar joten su lugar, que no está instalado por defecto en la mayoría de los sistemas GNU / Linux). Puedes usar un bucle conexpr o una sustitución aritmética para aumentar aún más la portabilidad, si necesita:

#!/bin/sh

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

Eso usa un whilebucle con el [comando para continuar haciendo bucles solo cuando $iestá dentro del rango.


En lugar de imprimir todo el alfabeto, su script define una variable ne imprime las primeras $nletras minúsculas. Aquí hay una versión de su script que no se basa en características específicas de Bash y funciona en Dash, pero requiere seq:

#!/bin/sh

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

Ajustar el valor de los ncambios cuántas letras se imprimen, como en su secuencia de comandos.

Aquí hay una versión que no requiere seq:

#!/bin/sh

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

Hay $stopuno más alto que el código de caracteres de la última letra que debe imprimirse, por lo que uso -lt(menor que) en lugar de -le(menor o igual) con el [comando. (También habría funcionado para hacer stop=$((i + n - 1))y usar [ $i -le $stop ]).

Eliah Kagan
fuente
1
Esta es una respuesta fenomenalmente detallada que usted para la educación. Soy un principiante, así que mi forma de escribir guiones es reunir elementos de trabajo que se encuentran en Internet hasta que funcione. No tengo dudas de que hay 1) mejores y 2) formas más simples de hacer las cosas que estoy creando con scripts y lo anterior ayuda mucho con eso.
denski
Relacionado: stackoverflow.com/questions/169511/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件