Bash IFS игнорирует разделитель в конце строки - PullRequest
2 голосов
/ 29 января 2020

У меня есть файл с полными парами ключ-значение. Я написал этот сценарий оболочки, который читает каждую строку и значение ключа разделения.

while IFS='=' read -r key value
do
   something

done < < application.properties.

Одно из свойств выглядит следующим образом Connections/Database/Token=#!VWdg5neXrFiIbMxtAzOwmH+fM2FNtk6QPLhgOHw=

Как запустить мой сценарий, его разделение это нормально, но его игнорируя символ = в конце строки.

Он дает

key = Connections/Database/Token
value = #!VWdg5neXrFiIbMxtAzOwmH+fM2FNtk6QPLhgOHw

, но должен давать как:

 key = Connections/Database/Token
 value = #!VWdg5neXrFiIbMxtAzOwmH+fM2FNtk6QPLhgOHw=

Ответы [ 2 ]

3 голосов
/ 29 января 2020

TL; DR Добавьте явное = в конец каждой строки ввода, затем удалите его из результирующего значения перед его использованием.


Почему это работает так, как работает

См. https://mywiki.wooledge.org/BashPitfalls#pf47. Короче говоря, = в IFS не рассматривается как поле разделитель , а как поле терминатор , согласно определению POSIX разделения поля .

Когда вы пишете

IFS== read -r key value <<< "foo=var="

, вход сначала разбивается на два поля, "foo" и "var" (не "foo", "var", а также ""). Переменных ровно столько же, сколько и полей, поэтому вы просто получите key = foo и value = var

Если у вас есть

IFS== read -r key value <<< "foo=var=="

, теперь есть три поля: "foo", "var" и "". Поскольку существует только две переменные, тогда key = foo и присваивается значение:

  1. значение «var», как обычно
  2. Разделитель «=» сразу после «var» на входе
  3. Поле "" с входа
  4. Разделитель "=" после "" на входе

См. спецификацию POSIX для read для получения подробной информации о каждой переменной read присваивается значение после разделения поля на входе.

Таким образом, никогда не бывает завершающего нулевого поля, которое получается в результате разделения поля input, только конечный разделитель , который добавляется обратно к конечной переменной.


Как сохранить ввод

Чтобы обойти это, добавьте явный = на ваш вход, а затем удалите его из результирующего значения.

$ for input in "foo=bar" "foo=bar=" "foo=bar=="; do
> IFS== read -r name value <<< "$input="
> echo "${value%=}"
> done
bar
bar=
bar==

В вашем случае это означает использование чего-то

while IFS='=' read -r key value
do
   value=${value%=}
   ...    
done < < (sed 's/$/=/' application.properties)

Или, как предложено сначала Иваном , используйте операторы расширения параметров, чтобы разделить ввод вместо того, чтобы read do it.

while read -r input; do
    key=${input%%=*}
    value=${input#*=}
    ...
done < application.properties

В любом случае, имейте в виду, что в качестве разделителя здесь рассматривается только * * =; вам нужно обрезать конечный пробел по ключу и начальный пробел по значению, если ваши свойства выглядят как name = value, а не name=value.

1 голос
/ 29 января 2020

Метод IFS определенно не подходит здесь. Попробуйте вместо этого.

while read -r item
do
    key="${item%%=*}"
    val="${item#*=}"
    echo "key = $key"
    echo "value = $val"
done < file

Ну, может быть, IFS может работать так же, как и так

cat -E file | while IFS== read -r key value
do
    echo "key = $key"
    echo "value = ${value%$}"
done

Я на Ubuntu 18.04.3 LTS, GNU bash, версия 4.4.20 ( 1) -release (x86_64-p c - linux -gnu) Использовал этот тестовый файл

$ cat file
Connections/Database/Token=#!VWdg5neXrFiIbMxtAzOwmH+fM2FNtk6QPLhgOHw1=
Conn/Database/Token=#!VWdg5neXrFiIbMxtAzOwmH+fM2FNtk6QPLhgOHw2
Connections/Data/Token=#!VWdg5neXrFiIbMxtAzOwmH+fM2FNtk6QPLhgOHw3=
Connection/Data/Token=#!VWdg5neXrFiIbMxtAzOwmH+fM2FNtk6QPLhgOHw3=test

И я использовал эти команды, чтобы увидеть, что происходит

cat -E file | while IFS== read -r key value last; do echo "key = $key"; echo "value = $value"; echo "last = ${last-empty}"; done
cat -E file | while IFS== read -r key value;      do echo "key = $key"; echo "value = $value"; echo "last = ${last-empty}"; done
cat    file | while IFS== read -r key value last; do echo "key = $key"; echo "value = $value"; echo "last = ${last-empty}"; done
cat    file | while IFS== read -r key value;      do echo "key = $key"; echo "value = $value"; echo "last = ${last-empty}"; done
...