私は 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.
スクリプトで行ごとに分割し、一度に 1 行ずつ印刷する:
#!/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」演算子と呼ばれることもあります。これは次のことを行います。
- 変数の値を取得します。これは文字列です。
- この値をフィールド区切り文字ごとに分割します。フィールド区切り文字は変数の値に含まれる文字です
IFS
。デフォルトでは、この変数にはスペース、タブ、改行(変数が設定されていない場合にもこのデフォルトが使用されます。)結果は文字列のリストになります。 - リストの各要素をワイルドカード (glob) パターンとして扱います。1 つ以上のファイル名に一致する場合、その要素は一致するファイル名のリストに置き換えられます。それ以外の場合、要素はそのまま残されます。
たとえば、ディレクトリにfoo
、、bar
およびというファイルが含まれている場合baz
、次のスクリプトは、、およびの 3 行を出力a*
しbar
ますbaz
。
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 バリアントに適用されます。
答え2
行を読み込むには、シェルの組み込みの読む関数、
$ヘルプを読む | ヘッド -2
読み取り: 読み取り [-ers] [-u fd] [-t タイムアウト] [-p プロンプト] [-a 配列] [-n nchars] [-d 区切り文字] [名前...]
標準入力から1行読み込まれるか、-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