Bash IFS, получить zapped в вызове функции - PullRequest
3 голосов
/ 13 февраля 2020

Платформа CentOS Linux, версия 7.6.1810, работает в bash. GNU bash, версия 4.2.46 (2) -релиз (x86_64-redhat- linux -gnu)

Это идиома, которую я видел рекомендуемой для анализа текста в bash в целом и в частности, для возврата нескольких значений из функции.

IFS=":" read A B <<< $(echo ONE:TWO)

Я получаю неожиданное поведение при вызове функции, ггг в приведенном здесь примере

IFS=":" read Y1 Y2 <<< $(yyy)

where yyy сам также хочет сделать аналогичный вызов.

Эффект заключается в том, что в yyy (), хотя я явно указываю IFS

 IFS=":" read C1 C2 <<< $( echo "A:B" )

поля разбираются, но оба значения присваиваются С1, он получает значение "AB". Если функция вызывается изолированно, она работает как положено.

Это тестовый пример, извлеченный из гораздо более крупного скрипта. Я хочу знать, что здесь происходит с IFS. В случае сбоя (второй пример ниже) установка IFS = ":" в вызывающей стороне как-то приводит к агрегированию полей результата. Первый и третий вызовы yyy () ниже работают как положено, вывод показывается после кода.

#!/bin/bash

debug() { echo "$1" 1>&2 ; }

yyy() {
    debug "in yyy"
    # why are the two values assigned to A here if the caller specified IFS?
    IFS=":" read A B <<< $(echo ONE:TWO)
    debug "A=$A"
    debug "B=$B"

    echo "$A:$B"
}

    # this works as expected
    read Y1 Y2 <<< $(yyy)
    echo -e "===\n"

    # this cause the read in yyy() to aggregate
    IFS=":" read Y1 Y2 <<< $(yyy)
    echo -e "===\n"

    # This is a workaround that enables yyy() to work correctly
    # But why do I need to do this?
    OUT="$(yyy)"
    IFS=":" read Y1 Y2 <<< $(echo $OUT)

Это вывод

in yyy 
A=ONE B=TWO

===

in yyy 
A=ONE TWO B=

===

in yyy 
A=ONE B=TWO

Обратите внимание, что во втором случае A получает значение ONE TWO

1 Ответ

2 голосов
/ 13 февраля 2020

Похоже, что это ошибка в bash-4.2, как обсуждалось здесь, IFS неправильно разбивает строки в bash 4.2 . Должно работать на версиях выше.

Это результаты той же версии, что и у вас - GNU bash, версия 4.2.46 (2). Когда я запустил функцию yyy в режиме отладки (установив set -x в командной строке).

++ IFS=:
++ read A B
+++ echo ONE:TWO
++ debug 'A=ONE TWO'
++ echo 'A=ONE TWO'
A=ONE TWO
++ debug B=
++ echo B=
B=
++ echo 'ONE TWO:'

Выше приведен фрагмент выходных данных из режима отладки. Как вы можете видеть, когда echo ONE:TWO печатается в результате подстановки команды, расщепление слов не ожидается, потому что строка не содержит символов по умолчанию IFS (пробел / табуляция или символ новой строки)

Таким образом, вы ожидаете, что чтение всей строки с IFS=:, как ожидается, разделит строку и поместит значения в составляющие переменные A и B, но каким-то образом символ : будет потерян и строка ONE TWO сохраняется в качестве первого значения переменной.

Посмотрите на результат выполнения функции в GNU bash, версия 4.4.12 (1), которая демонстрирует правильное поведение.

++ IFS=:
++ read A B
+++ echo ONE:TWO
++ debug A=ONE
++ echo A=ONE
A=ONE
++ debug B=TWO
++ echo B=TWO
B=TWO
++ echo ONE:TWO

Было много IFS связанных ошибок до версии 4.4.0 bash / CHANGES . Поэтому персональная рекомендация - обновить версию bash до более новой стабильной. Также см. Попытка разбить строку на две переменные

Аналогичная ошибка в версии 4.4.0 (1) -релиз

Можно ожидать, что ONE:TWO останется неизменным при расширении $(..) по причинам, упомянутым ранее. Но и здесь символ-разделитель теряется, а переменная A устанавливается на ONE TWO

IFS=":" read A B <<< $(echo ONE:TWO)
echo "$A"
ONE TWO

Удивительно, но приведенный выше код работает на 4.2.46 (2), что означает 4.4.0 (1). ) сломал функционал, который работал в более ранних выпусках.

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