
Рекомендуется использовать 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
желательно. WASA?
решение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[newline]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
решение2
Я не знаю, в чем причина, но 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"
который печатает правильно.