将命令的输出读取到 shell 中的变量中,在每个空格处分割文本,而不仅仅是换行符

将命令的输出读取到 shell 中的变量中,在每个空格处分割文本,而不仅仅是换行符

我在 FreeBSD 11 上使用 sh (不是 bash/csh),但我不明白这一点:

在控制台中

命令:zpool status -v

结果:

  pool: My_pool
 state: ONLINE
status: One or more devices is currently being resilvered.  The pool will
        continue to function, possibly in a degraded state.
action: Wait for the resilver to complete.

在脚本中按行分割并一次打印行:

#!/bin/sh
zp="$(zpool status -v)"
for line in $zp; do
  echo "$line%"
done

结果:

pool:%
My_pool%
state:%
ONLINE%
status:%
One%
or%
more%
devices%
is%
currently%
being%
resilvered.%
The%
pool%
will%
continue%
to%
function,%
possibly%
in%
a%
degraded%
state.%
action:%
Wait%

根据我能找到的所有信息,我使用的语法对于 sh 来说是正确的,并且应该一次读取一行,而不是一次读取一个单词。

我缺少什么?

答案1

我对你发现的内容感到困惑。你使用的语法对于 sh 和在空格处分割是正确的,而不仅仅是换行符。这已被广泛记录。认为它在换行符处分裂并不是一个流行的误解。 (不理解它确实分裂是一个普遍的误解。)

更准确地说,不加引号的变量扩展(如 )$zp有时被称为“split+glob”运算符。它的作用是:

  1. 取变量的值。那是一个字符串。
  2. 在每个字段分隔符处拆分此值。字段分隔符是变量值中的分隔符IFS;默认情况下,该变量包含一个空格、制表符和换行符。 (如果未设置变量,也会使用此默认值。)结果是字符串列表。
  3. 将列表中的每个元素视为通配符 (glob) 模式。如果它与一个或多个文件名匹配,则该元素将被替换为匹配文件名的列表。否则该元素将被保留。

例如,如果目录包含名为foobar和 的文件baz,则以下脚本将打印三行:a*barbaz

zq='a* b*'
for word in $zq; do echo "$word"; done

如果您想在换行符处拆分字符串,则可以通过设置IFS换行符并禁用通配符扩展来实现。

#!/bin/sh
set -f
IFS='
'
zq=…
for line in $zq; do
done

这适用于所有 sh 和 csh 变体。

也可以看看为什么我的 shell 脚本会因为空格或其他特殊字符而卡住?

答案2

要读取行,请使用 shell 的内置命令功能,

$帮助阅读 |头-2

读取:读取 [-ers] [-u fd] [-t 超时] [-p 提示符] [-a 数组] [-n nchars] [-d delim] [名称 ...]

从标准输入读取一行,如果提供了 -u 选项,则从文件描述符 FD 读取一行

把它放在一个while循环中,瞧,你有几行:

$ wc -l -w ~/.profile 
  24      47 /Users/jklowden/.profile
$ for W in $(cat ~/.profile); do echo $W; done | wc -l 
  47
$ while read L; do echo $L; done < ~/.profile | wc -l 
  24

相关内容