Bash IFS 忽略行尾的定界符
Bash IFS is ignoring the delimiter at the end of line
我有一个充满键值对的文件。我写了这个 shell 脚本,它读取每一行并拆分键值。
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=
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-pc-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
TL;DR 将显式 =
添加到每个输入行的末尾,然后在使用它之前将其从结果值中删除。
为什么它会这样工作
参见 https://mywiki.wooledge.org/BashPitfalls#pf47. In short, the =
in IFS is not treated as a field separator, but a field terminator, according to the POSIX definition of field-splitting。
写的时候
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,赋值:
- 值"var",正常
- 输入"var"后的定界符“=”
- 来自输入的字段“”
- 输入中“”后面的分隔符“=”
有关每个变量的详细信息,请参阅 POSIX specification for read
read
在输入进行字段拆分后为其分配一个值。
因此,永远不会有因字段拆分输入而产生的尾随空字段,
只有一个尾随 分隔符 被添加回最终变量。
如何保存输入
要解决此问题,请在您的输入中添加一个显式 =,然后将其从结果值中删除。
$ 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
这样做。
while read -r input; do
key=${input%%=*}
value=${input#*=}
...
done < application.properties
无论哪种方式,请记住,仅 =
被视为此处的分隔符;如果您的属性看起来像 name = value
而不是 name=value
.
,您可能需要 trim 键的尾随空格和值的前导空格
我有一个充满键值对的文件。我写了这个 shell 脚本,它读取每一行并拆分键值。
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=
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-pc-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
TL;DR 将显式 =
添加到每个输入行的末尾,然后在使用它之前将其从结果值中删除。
为什么它会这样工作
参见 https://mywiki.wooledge.org/BashPitfalls#pf47. In short, the =
in IFS is not treated as a field separator, but a field terminator, according to the POSIX definition of field-splitting。
写的时候
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,赋值:
- 值"var",正常
- 输入"var"后的定界符“=”
- 来自输入的字段“”
- 输入中“”后面的分隔符“=”
有关每个变量的详细信息,请参阅 POSIX specification for read
read
在输入进行字段拆分后为其分配一个值。
因此,永远不会有因字段拆分输入而产生的尾随空字段, 只有一个尾随 分隔符 被添加回最终变量。
如何保存输入
要解决此问题,请在您的输入中添加一个显式 =,然后将其从结果值中删除。
$ 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
这样做。
while read -r input; do
key=${input%%=*}
value=${input#*=}
...
done < application.properties
无论哪种方式,请记住,仅 =
被视为此处的分隔符;如果您的属性看起来像 name = value
而不是 name=value
.