it-swarm-ru.tech

Как я могу читать построчно из переменной в Bash?

У меня есть переменная, которая содержит многострочный вывод команды. Какой самый эффективный способ прочитать вывод построчно из переменной?

Например:

jobs="$(jobs)"
if [ "$jobs" ]; then
    # read lines from $jobs
fi
53
Eugene Yarmash

Вы можете использовать цикл while с подстановкой процесса:

while read -r line
do
    echo "$line"
done < <(jobs)

Оптимальный способ чтения многострочной переменной - установить пустую переменную IFS и ​​printf переменную с завершающим символом новой строки:

# Printf '%s\n' "$var" is necessary because printf '%s' "$var" on a
# variable that doesn't end with a newline then the while loop will
# completely miss the last line of the variable.
while IFS= read -r line
do
   echo "$line"
done < <(printf '%s\n' "$var")

Примечание. Согласно shellcheck sc2031 использование подстановки процесса предпочтительнее конвейера, чтобы избежать [тонкого] создания подоболочки.

Также имейте в виду, что при присвоении имени переменной jobs это может привести к путанице, поскольку это также имя обычной команды Shell.

70
dogbane

Чтобы обработать вывод командной строки построчно ( пояснение ):

_jobs |
while IFS= read -r line; do
  process "$line"
done
_

Если у вас уже есть данные в переменной:

_printf %s "$foo" | …
_

_printf %s "$foo"_ практически идентичен _echo "$foo"_, но печатает _$foo_ буквально, тогда как _echo "$foo"_ может интерпретировать _$foo_ в качестве опции для команды echo, если она начинается с _-_ и может расширять последовательности обратной косой черты в _$foo_ в некоторых оболочках.

Обратите внимание, что в некоторых оболочках (ash, bash, pdksh, но не в ksh или zsh) правая часть конвейера выполняется в отдельном процессе, поэтому любая переменная, заданная в цикле, теряется. Например, следующий скрипт подсчета строк выводит 0 в этих оболочках:

_n=0
printf %s "$foo" |
while IFS= read -r line; do
  n=$(($n + 1))
done
echo $n
_

Обходной путь - поместить оставшуюся часть скрипта (или хотя бы ту часть, которая нуждается в значении _$n_ из цикла) в список команд:

_n=0
printf %s "$foo" | {
  while IFS= read -r line; do
    n=$(($n + 1))
  done
  echo $n
}
_

Если действия с непустыми строками достаточно хороши и ввод невелик, вы можете использовать разбиение Word:

_IFS='
'
set -f
for line in $(jobs); do
  # process line
done
set +f
unset IFS
_

Объяснение: установка IFS на одну новую строку приводит к тому, что разбиение Word происходит только на новых строках (в отличие от любого символа пробела при настройке по умолчанию). _set -f_ отключает глобализацию (то есть расширение по шаблону), которое в противном случае могло бы произойти с результатом подстановки команды $(jobs) или переменной substitutino _$foo_. Цикл for действует на все части $(jobs), которые являются непустыми строками в выводе команды. Наконец, восстановите настройки globbing и IFS.

28
Gilles 'SO- stop being evil'

Проблема: если вы используете цикл while, он будет работать в подоболочке и все переменные будут потеряны. Решение: использовать для цикла

# change delimiter (IFS) to new line.
IFS_BAK=$IFS
IFS=$'\n'

for line in $variableWithSeveralLines; do
 echo "$line"

 # return IFS back if you need to split new line by spaces:
 IFS=$IFS_BAK
 IFS_BAK=
 lineConvertedToArraySplittedBySpaces=( $line )
 echo "{lineConvertedToArraySplittedBySpaces[0]}"
 # return IFS back to newline for "for" loop
 IFS_BAK=$IFS
 IFS=$'\n'

done 

# return delimiter to previous value
IFS=$IFS_BAK
IFS_BAK=
16
Dima
jobs="$(jobs)"
while IFS= read
do
    echo $REPLY
done <<< "$jobs"

Ссылки:

11
l0b0

В последних версиях bash используйте mapfile или readarray для эффективного считывания вывода команды в массивы

$ readarray test < <(ls -ltrR)
$ echo ${#test[@]}
6305

Отказ от ответственности: ужасный пример, но вы можете придумать лучшую команду для использования, чем вы сами

10
sehe

Вы можете использовать <<< для простого чтения из переменной, содержащей разделенные строкой данные:

while read -r line
do 
  echo "A line of input: $line"
done <<<"$lines"
0
Matthew Herrmann