Bashの変数から1行ずつ読み込むには?

Bashの変数から1行ずつ読み込むには?

コマンドの複数行出力を含む変数があります。変数から出力を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

jobs="$(jobs)"
while IFS= read -r line
do
    echo "$line"
done <<< "$jobs"

引用:

答え4

最新のbashバージョンでは、またはmapfileを使用してreadarrayコマンド出力を配列として効果的に読み込みます。

$ readarray test < <(ls -ltrR)
$ echo ${#test[@]}
6305

免責事項:ひどい例ですが、おそらくlsよりも良いコマンドを思い出すことができます。

関連情報