將命令的輸出讀取到 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

我對你發現的內容感到困惑。這已被廣泛記錄。認為它在換行符號處分裂並不是一個流行的誤解。 (不理解它確實分裂是一個普遍的誤解。)

更準確地說,不加引號的變數擴展(如 )$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

相關內容