
Se recomienda utilizar IFS para recorrer listas separadas por comas, por ejemploaquí. Pero veo que
sentences="Hello World,Questions"
IFS=, sentences1=($sentences) # if we comment this line then loop is fixed
sentences2=$sentences
for sentence in ${sentences2[@]};
do
for i in $(seq 1 2);
do
#This is fine - prints a new word every line
#echo $sentence
#This is fine - prints new number every line
#echo $i
echo $i $sentence
done
done
produce
1
2 Hello World
1
2 Question
En lugar de
1 Hello World
2 Hello World
1 Question
2 Question
deseado. ¿ERA UN?
Respuesta1
El principal problema aquí es que la segunda línea, IFS=, sentences1=($sentences)
establece IFS en "," permanentemente; esto significa que obtendrá un comportamiento de análisis extraño e inesperado durante el resto del script. Tenga en cuenta que con algo como IFS=, read ...
, esto no sucedería porque la asignación IFS se considera aplicable solo al read
comando; pero en IFS=, sentences1=($sentences)
, no hay un comando real, solo asignaciones, por lo que las asignaciones se tratan como si se aplicaran a todo el shell.
Entonces, echemos un vistazo a lo que sucede en el resto del guión. sentences2=$sentences
solo se copia sentences
en sentences2
, por lo que for sentence in ${sentences2[@]}
es un poco extraño. sentences2
no es una matriz (solo una cadena simple), por lo que el [@]
bit no hace nada, pero como IFS sigue siendo ",", se "divide en palabras" en "Hola mundo" y "Preguntas", es decir, obtener el resultado correcto, pero por el motivo equivocado.
(Nota: si fuera una matriz real, querrás tener comillas dobles alrededor, para for sentence in "${sentences2[@]}"
evitar que los elementos se dividan en palabras).
El siguiente comando, for i in $(seq 1 2)
es donde comienza el verdadero problema. seq 1 2
imprime "1[nueva línea]2[nueva línea]", la $( )
construcción recorta la nueva línea final y luego "1[nueva línea]2" se divide en palabras, pero como no hay coma allí, se trata como una sola palabra (eso sucede con contener una nueva línea). Por lo tanto, el bucle interno se ejecuta solo una vez, con i
el valor "1[nueva línea]2".
La siguiente línea, echo $i $sentence
imprime "1[nueva línea]2" seguido de un espacio, seguido del contenido de sentence
. En la primera iteración, con sentence
el valor "Hola mundo", esto imprime:
1
2 Hello World
... la nueva línea en el medio hace que parezca que se están imprimiendo dos cosas, pero en realidad es solo una echo
que contiene una nueva línea.
Entonces, dos grandes recomendaciones: primero, cuando cambie IFS, vuélvalo a cambiar después. En segundo lugar, siempre coloque comillas dobles en las referencias variables para evitar la división inesperada de palabras (y la expansión de comodines). Esto es lo que obtengo por el guión:
#!/bin/bash
sentences="Hello World,Questions"
saveIFS="$IFS"
IFS=, sentences1=($sentences)
IFS="$saveIFS" # Set IFS back to normal!
for sentence in "${sentences1[@]}"; do # Note double-quotes, and sentences1 is
an actual array
for i in $(seq 1 2); do # No double-quotes, we *want* seq's output to be spli
t
echo "$i" "$sentence" # Double-quotes for safety
done
done
Respuesta2
No sé cuál es el motivo pero irc://freenode/bash proporciona la solución
while IFS=, read -ra items; do
for item in "${items[@]}"; do
for i in {1..3}; do
printf '%d %s\n' "$i" "$item"
done
done
done <<< "Hello World,Questions,Answers"
que se imprima correctamente.