コマンドの複数行出力を含む変数があります。変数から出力を1行ずつ読み取る最も効率的な方法は何ですか?
たとえば、
jobs="$(jobs)"
if [ "$jobs" ]; then
# read lines from $jobs
fi
答え1
プロセス置換にはwhileループを使用できます。
while read -r line
do
echo "$line"
done < <(jobs)
複数行変数を読み取る最善の方法は、空のIFS
変数を設定し、printf
変数に末尾の改行文字を追加することです。
# Printf '%s\n' "$var" is necessary because printf '%s' "$var" on a
# variable that doesn't end with a newline then the while loop will
# completely miss the last line of the variable.
while IFS= read -r line
do
echo "$line"
done < <(printf '%s\n' "$var")
注:によるとシェルチェック sc2031、パイプの代わりにプロセス置換を使用して、サブシェルの生成を[微妙に]防止します。
また、変数名を指定するとjobs
一般的なシェルコマンドの名前でもあるため、混乱を招く可能性があります。
答え2
コマンドラインの出力を1行ずつ処理します(説明する):
jobs |
while IFS= read -r line; do
process "$line"
done
変数にすでにデータがある場合:
printf %s "$foo" | …
printf %s "$foo"
とほぼ同じですecho "$foo"
が$foo
、文字通り印刷されますが、で始まるとecho "$foo"
echoコマンドのオプションとして解釈され、一部のシェルではバックスラッシュシーケンスが拡張される可能性があります。$foo
-
$foo
一部のシェル(ash、bash、pdksh、ksh、またはzshを除く)では、パイプの右側が別々のプロセスで実行されるため、ループに設定したすべての変数が失われます。たとえば、次の行計算スクリプトはこれらのシェルからゼロを印刷します。
n=0
printf %s "$foo" |
while IFS= read -r line; do
n=$(($n + 1))
done
echo $n
$n
解決策は、スクリプトの残りの部分(または少なくともループに値が必要な部分)をコマンドのリストに入れることです。
n=0
printf %s "$foo" | {
while IFS= read -r line; do
n=$(($n + 1))
done
echo $n
}
空でない行に対する操作が十分で、入力がそれほど大きくない場合は、単語分割を使用できます。
IFS='
'
set -f
for line in $(jobs); do
# process line
done
set +f
unset IFS
説明:IFS
単一の改行に設定すると、改行でのみ単語が区切られます(デフォルト設定の空白文字の代わりに)。set -f
コマンド置換または変数置換$(jobs)
の結果として生じるワイルドカード(つまり、ワイルドカード拡張)をオフにします$foo
。ループは、コマンド出力for
のすべての部分$(jobs)
、つまりコマンド出力の空白ではなく、すべての行で動作します。最後に、ワイルドカードとIFS
設定をデフォルト値に復元します。
答え3
答え4
最新のbashバージョンでは、またはmapfile
を使用してreadarray
コマンド出力を配列として効果的に読み込みます。
$ readarray test < <(ls -ltrR)
$ echo ${#test[@]}
6305
免責事項:ひどい例ですが、おそらくlsよりも良いコマンドを思い出すことができます。