【问题标题】:Bash: read lines into an array *without* touching IFSBash:将行读入数组*无需*接触 IFS
【发布时间】:2014-03-19 21:28:57
【问题描述】:

我正在尝试将子shell 的输出行读入一个数组,但我不愿意设置 IFS,因为它是全局的。我不希望脚本的一部分影响以下部分,因为这是不好的做法,我拒绝这样做。在命令之后恢复 IFS 不是一个选项,因为在编辑脚本后将恢复保持在正确的位置太麻烦了。我如何向 bash 解释我希望每个数组元素包含一整行,而不必设置任何会破坏未来命令的全局变量?

这是一个显示 IFS 不需要的粘性的示例:

lines=($(egrep "^-o"  speccmds.cmd))
echo "${#lines[@]} lines without IFS"

IFS=$'\r\n' lines=($(egrep "^-o" speccmds.cmd))
echo "${#lines[@]} lines with IFS"

lines=($(egrep "^-o"  speccmds.cmd))
echo "${#lines[@]} lines without IFS?"

输出是:

42 lines without IFS
6 lines with IFS
6 lines without IFS?

【问题讨论】:

  • 您可以保存 IFS 的值并在完成“特殊”值后恢复它。这做了很多。
  • 即使保存和恢复也比必要的工作要多 - 您可以完全以 read 命令本身的本地方式设置 IFS。
  • 听起来您正在使用IFS=$'\n'; array=( $value ) 来拆分值。这不能正常工作,除非你也 set -f。改用 mapfile 或 while 读取循环。
  • 确实——array=( $(command) ) 是邪恶的。永远不要那样做。
  • @ByronHawkins,不,它不符合您的要求。看看当$(command) 在其输出中包含* 时会发生什么情况,该IFS 中的字符包围着它。 (...或者不是IFS的字符包围,如果你启用了shopt -s nullglob;在这种情况下,带有方括号的输出也会充满惊喜)。

标签: arrays bash


【解决方案1】:

这个问题可能是基于一个误解。

IFS=foo read 不会在读取操作本身之外更改 IFS。

因此,这会产生副作用,应该避免:

IFS=
declare -a array
while read -r; do
  array+=( "$REPLY" )
done < <(your-subshell-here)

...但这完全没有副作用:

declare -a array
while IFS= read -r; do
  array+=( "$REPLY" )
done < <(your-subshell-here)

对于 bash 4.0 或更高版本,还可以选择 readarraymapfile(相同操作的同义词):

mapfile -t array < <(your-subshell-here)

在稍后添加到您的答案中的示例中,您的代码如下:

lines=($(egrep "^-o"  speccmds.cmd))

更好的写法是:

mapfile -t lines < <(egrep "^-o"  speccmds.cmd)

【讨论】:

  • 我只是添加了一个例子来澄清。在我尝试使用的情况下,IFS 具有不必要的粘性。
  • @ByronHawkins,一般的答案是“不要那样做”——也就是说,没有充分的理由改变 IFS 的全局值。我上面给出的例子提供了不这样做的具体方法。如果您给出了一个更明确的用例示例,我可以提供一种更明确的方式来再次不依赖 IFS 的粘性使用。
  • 啊。你确实添加了一个例子。不要那样做;请改用read -amapfile
  • 还有一个问题:我在 Windows 上的 msys 中(已经有问题了......)而且我只有 bash 3.1,所以没有 mapfile。使用read -a 不适用于子shell 输出,因为子shell 在返回之前会吃掉换行符。有没有其他方法可以获得整行?
  • @ByronHawkins,第二个代码块(“完全没有副作用”)将给出整行;与 mapfile/readarray 相比,它的唯一缺点是简洁性降低。顺便说一句,如果您想使用 NUL 分隔的输入而不是换行符,唯一需要更改的是将 -d '' 添加到 read 命令。
【解决方案2】:

您是要将输出的 存储在数组中,还是 每个 行的 单词

  1. mapfile -t arrayname < <(your subshell)
    

    这根本不使用 IFS。

  2. 单词

    (your subshell) | while IFS=: read -ra words; do ...
    

    var=value command args...形式将var变量放入command的环境中,不影响当前shell的环境。

【讨论】:

    猜你喜欢
    • 2014-04-03
    • 2021-04-21
    • 2022-01-22
    • 1970-01-01
    • 2011-05-22
    • 1970-01-01
    • 2015-08-28
    • 2012-07-08
    • 2022-01-17
    相关资源
    最近更新 更多