Цикл по переменной с переносами строк без подсессии

Вопрос или проблема

Я пытаюсь пройтись по переменной, которая содержит переносы строк, совместимым с POSIX способом.

Следующий вариант работает:

echo "$REQUIRE_PURPOSE" | while IFS= read -r line ; do
  echo "$line"
done

но конвейер запускает другую оболочку.

Я попробовал:

OLDIFS="$IFS"
IFS='
'
for line in "$REQUIRE_PURPOSE"; do
  echo "$line"
done
IFS="$OLDIFS"

Но это просто выводит содержимое "$REQUIRE_PURPOSE"

Я также пробовал:

while IFS= read -r line ; do
  echo "$line"
done <<< "$REQUIRE_PURPOSE"

но и здесь это не работает.

Пример:

echo "$REQUIRE_PURPOSE"
echo "---"
while IFS= read -r line ; do echo "$line"; done <<< "$REQUIRE_PURPOSE"

генерирует:

uuu
iii
Цифровая подпись
---
uuuniiinЦифровая подпись

или

00000000  75 75 75 0a 69 69 69 0a  44 69 67 69 74 61 6c 20  |uuu.iii.Дигит. |
00000010  53 69 67 6e 61 74 75 72  65 0a 2d 2d 2d 0a 31 20  |Подпись.---.1 |
00000020  75 75 75 6e 69 69 69 6e  44 69 67 69 74 61 6c 20  |uuuniiinЦифров. |
00000030  53 69 67 6e 61 74 75 72  65 0a 75 75 75 0a 69 69  |Подпись.uuu.ii|
00000040  69 0a 44 69 67 69 74 61  6c 20 53 69 67 6e 61 74  |i.Цифровая Подп.|
00000050  75 72 65 0a                                       |ь.|
00000054

Есть идеи?

Опция для read, которая вам нужна, – это -a. Она разобьет ввод по IFS на массив:

read -a lines <<<"$REQUIRE_PURPOSE"
for i in "${lines[@]}"
do
    echo "$i"
done

Вы можете сделать это с помощью расширения параметров (S.2.6.2) и подстановки команд (S.2.6.3) внутри некотированного HereDoc (S2.7.4):

VAR="$(echo Line1; echo Line2; echo EOF; echo "HereDoc продолжается."; echo Line5)"
while read line; do
 printf "Строка-%s-КОНЕЦ\n" "$line"
done << EOF
$VAR
EOF
while read line; do
 printf "Строка-%s-КОНЕЦ\n" "$line"
done << EOF
$(
 echo Line1
 echo Line2
 echo EOF
 echo "HereDoc продолжается."
 echo Line5
)
EOF

Ответ или решение

Эффективный подход к перебору переменной с разделителями строки в POSIX

При работе с переменными, содержащими новые строки, важно найти способ обходиться без лишних подсистем и обеспечивать правильную обработку данных. В этом ответе мы рассмотрим, как можно перебирать строки переменной, используя минималистичные и эффективные подходы с учетом стандартов POSIX.

Проблема

Исходная проблема заключается в том, что попытки использовать while IFS= read -r line; do ...; done <<< "$REQUIRE_PURPOSE" приводят к некорректному выводу. Это связано с неправильным использованием новых строк и разделителей. Кроме того, использование конвейеров (|) приводит к запуску новой подсистемы, что нежелательно в данном контексте.

Решения

  1. Использование массива с опцией -a для read:
    Один из эффективных способов обхода проблемы — это загрузка содержимого переменной в массив с помощью команды read:

    read -a lines <<< "$REQUIRE_PURPOSE"
    for line in "${lines[@]}"; do
       echo "$line"
    done

    В этом варианте содержимое переменной $REQUIRE_PURPOSE разбивается на строки на основе значений IFS, которые по умолчанию равны пробелам и символам новых строк. Это позволяет перебирать строки в массиве без подсистем.

  2. Применение параметрической подстановки и командной подстановки:
    Вам также может подойти подход с использованием хердоков (here documents):

    VAR="$(echo Line1; echo Line2; echo EOF; echo "HereDoc продолжается."; echo Line5)"
    while read line; do
       printf "Line-%s-END\n" "$line"
    done << EOF
    $VAR
    EOF

    Этот пример демонстрирует, как можно использовать переменную VAR в комбинации с хердоком. Он работает, поскольку VAR может содержать любые строки, которые необходимо обработать.

  3. Использование цикла while и прямой подстановки:
    Если вы хотите избежать проверки массива, вы можете использовать прямую подстановку в цикле while:

    while read -r line; do
       echo "$line"
    done <<< "$REQUIRE_PURPOSE"

    Однако, как уже упоминалось, это создаёт дочерний процесс, что может быть нежелательно.

Заключение

Перебор переменной с новыми строками в зависимости от ваших требований можно осуществить различными способами, каждый из которых имеет свои плюсы и минусы. Использование массива с read -a является наиболее оптимальным методом, так как он соответствует стандартам POSIX и минимизирует накладные расходы. Если вам нужна гибкость и возможность обрабатывать сложные входные данные, подход с хердоками будет эффективен.

Для достижения наилучших результатов тестируйте каждое решение в контексте вашей конкретной среды и задач, чтобы выбрать наиболее подходящее.

Оцените материал
Добавить комментарий

Капча загружается...