Рекомендуется использовать IFS для просмотра списка, разделенного запятыми, например, здесь. Но я вижу, что

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

производит

1 
2 Hello World
1
2 Question

Вместо

1 Hello World
2 Hello World
1 Question 
2 Question

желательно. БЫЛ?

2 ответа2

1

Основная проблема здесь заключается в том, что вторая строка, IFS=, sentences1=($sentences) , устанавливает IFS на «,» навсегда; это означает, что вы получите странное и неожиданное поведение при разборе в оставшейся части скрипта. Обратите внимание, что с чем-то вроде IFS=, read ... этого не произойдет, потому что назначение IFS рассматривается как применение только к команде read ; но в IFS=, sentences1=($sentences) нет реальной команды, только назначения, поэтому назначения обрабатываются как применимые ко всей оболочке.

Итак, давайте посмотрим, что происходит в остальной части сценария. sentences2=$sentences просто копирует sentences в sentences2 , поэтому for sentence in ${sentences2[@]} это немного странно. sentences2 не является массивом (просто строка), поэтому бит [@] ничего не делает, но, поскольку IFS по-прежнему ",", он переводит слово "разбито на слова" в "Hello World" и "Questions" - то есть вы получаете правильный результат, но по неправильной причине.

(Примечание: если бы это был фактический массив, вы бы хотели заключить его в двойные кавычки, как, например, в for sentence in "${sentences2[@]}" чтобы элементы не разделялись на слова.)

Следующая команда for i in $(seq 1 2) - это то, с чего начинается настоящая проблема. seq 1 2 печатает «1 [newline] 2 [newline]», конструкция $( ) обрезает завершающий символ новой строки, а затем «1 [newline] 2» получает разделение слов - но так как там нет запятой, это рассматривается как одно слово (которое содержит новую строку). Таким образом, внутренний цикл выполняется только один раз, с i установленным в «1 [newline] 2».

Следующая строка, echo $i $sentence , печатает «1 [новая строка] 2», затем пробел, за которым следует содержание sentence . На первой итерации с sentence "Hello World" это печатает:

1 
2 Hello World

... новая строка в середине делает это похожим на две вещи, которые печатаются, но на самом деле это всего лишь одно echo которое содержит новую строку.

Итак, две большие рекомендации: во-первых, когда вы меняете IFS, измените его позже. Во-вторых, всегда используйте двойные кавычки для ссылок на переменные, чтобы избежать неожиданного разделения слов (и подстановочных знаков). Вот что я получаю за сценарий:

#!/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
0

Я не знаю, в чем причина, но irc://freenode/bash предоставляет решение

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"

это печатает правильно.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .