2011-05-16 12:14:48 +0000 2011-05-16 12:14:48 +0000
247
247

Бэш: Итерация по строкам в переменной

Как правильно выполнить итерацию по строкам в bash либо в переменной, либо из вывода команды? Простая настройка IFS переменной на новую строку работает для вывода команды, но не при обработке переменной, содержащей новые строки.

Например,

#!/bin/bash

list="One\ntwo\nthree\nfour"

#Print the list with echo
echo -e "echo: \n$list"

#Set the field separator to new line
IFS=$'\n'

#Try to iterate over each line
echo "For loop:"
for item in $list
do
        echo "Item: $item"
done

#Output the variable to a file
echo -e $list > list.txt

#Try to iterate over each line from the cat command
echo "For loop over command output:"
for item in `cat list.txt`
do
        echo "Item: $item"
done

Это дает результат:

echo: 
One
two
three
four
For loop:
Item: One\ntwo\nthree\nfour
For loop over command output:
Item: One
Item: two
Item: three
Item: four

Как видите, эхо переменной или итерация по команде cat корректно печатает каждую из строк по одной. Однако, первая для цикла распечатывает все элементы в одной строке. Есть идеи?

Ответы (5)

312
312
312
2011-05-16 13:47:36 +0000

С помощью bash, если вы хотите встраивать новые строки в строку, заключите строку в: $'':

$ list="One\ntwo\nthree\nfour"
$ echo "$list"
One\ntwo\nthree\nfour
$ list=$'One\ntwo\nthree\nfour'
$ echo "$list"
One
two
three
four

И если у вас уже есть такая строка в переменной, вы можете читать ее построчно с:

while IFS= read -r line; do
    echo "... $line ..."
done <<< "$list"
72
72
72
2011-05-16 12:21:28 +0000

Вы можете использовать while + read:

some_command | while read line ; do
   echo === $line ===
done

Btw. -e опция echo для printf является нестандартной. Используйте &007 вместо этого, если вы хотите портативность.

32
32
32
2014-03-17 21:45:33 +0000
#!/bin/sh

items="
one two three four
hello world
this should work just fine
"

IFS='
'
count=0
for item in $items
do
  count=$((count+1))
  echo $count $item
done
15
15
15
2011-05-16 12:45:14 +0000

Вот забавный способ сделать ваш цикл:

for item in ${list//\n/
}
do
   echo "Item: $item"
done

Немного более чувствительным/читаемым было бы:

cr='
'
for item in ${list//\n/$cr}
do
   echo "Item: $item"
done

Но это все слишком сложно, вам нужно только пространство:

for item in ${list//\n/ }
do
   echo "Item: $item"
done

Вы $line переменная не содержит новых строк. Она содержит экземпляры `Вот забавный способ сделать ваш цикл:

for item in ${list//\n/
}
do
   echo "Item: $item"
done

Немного более чувствительным/читаемым было бы:

cr='
'
for item in ${list//\n/$cr}
do
   echo "Item: $item"
done

Но это все слишком сложно, вам нужно только пространство:

for item in ${list//\n/ }
do
   echo "Item: $item"
done

Вы $line переменная не содержит новых строк. Она содержит экземпляры , за которыми следует n. Вы можете видеть это ясно с:

$ cat t.sh
#! /bin/bash
list="One\ntwo\nthree\nfour"
echo $list | hexdump -C

$ ./t.sh
00000000 4f 6e 65 5c 6e 74 77 6f 5c 6e 74 68 72 65 65 5c |One\ntwo\nthree\|
00000010 6e 66 6f 75 72 0a |nfour.|
00000016

Замена заменяет их пробелами, чего достаточно для работы с циклами:

$ cat t.sh
#! /bin/bash
list="One\ntwo\nthree\nfour"
echo ${list//\n/ } | hexdump -C

$ ./t.sh 
00000000 4f 6e 65 20 74 77 6f 20 74 68 72 65 65 20 66 6f |One two three fo|
00000010 75 72 0a |ur.|
00000013
  • *

Демонстрация:

$ cat t.sh
#! /bin/bash
list="One\ntwo\nthree\nfour"
echo ${list//\n/ } | hexdump -C
for item in ${list//\n/ } ; do
    echo $item
done

$ ./t.sh 
00000000 4f 6e 65 20 74 77 6f 20 74 68 72 65 65 20 66 6f |One two three fo|
00000010 75 72 0a |ur.|
00000013
One
two
three
four
3
3
3
2019-05-07 16:19:00 +0000

Вы также можете сначала преобразовать переменную в массив, а затем выполнить итерацию.

lines="abc
def
ghi"

declare -a theArray

while read -r line
do
    theArray+=($line)            
done <<< "$lines"

for line in "${theArray[@]}"
do
    echo "$line"
    #Do something complex here that would break your read loop
done

Это полезно только в том случае, если вы не хотите связываться с IFS, а также иметь проблемы с командой read, как это может случиться, если вы вызываете другой скрипт в цикле, что этот скрипт может опустошить буфер чтения перед возвращением, как это случилось со мной.