各反復の開始時に複数の変数を読み取り、ユーザーからの入力をループに読み取る必要があるユースケースがあります。
私が探索する方法がわからない解決策への可能な道-
... | while read ...
の代わりにfor
ループを使用する... for
ループ内に複数の変数を割り当てる方法がわかりません
echo -e "1 2 3\n4 5 6" |\
while read a b c;
do
echo "$a -> $b -> $c";
echo "Enter a number:";
read d ;
echo "This number is $d" ;
done
これが正しければ、基本的に値のリストをループし、次にループ内で別のread
をループする必要があると思います。
ここにいくつかのオプションがあります。1と2はおそらく最も健全です。
1。文字列で配列をエミュレートする
2D配列を持つことは良いことですが、Bashでは実際には不可能です。値に空白がない場合、近似するための1つの回避策は、3つの数値の各セットを文字列に貼り付け、ループ内で文字列を分割することです。
_for x in "1 2 3" "4 5 6"; do
read a b c <<< "$x";
read -p "Enter a number: " d
echo "$a - $b - $c - $d ";
done
_
もちろん、他のセパレータも使用できます。 _for x in 1:2:3 ...
_および_IFS=: read a b c <<< "$x"
_。
2。 stdinを解放するためにパイプを別のリダイレクトに置き換えます
別の可能性は、_read a b c
_を別のfdから読み取って、それに入力を送ることです(これは標準のシェルで動作するはずです)。
_while read a b c <&3; do
printf "Enter a number: "
read d
echo "$a - $b - $c - $d ";
done 3<<EOF
1 2 3
4 5 6
EOF
_
また、コマンドからデータを取得する場合は、ここでプロセス置換を使用することもできます:while read a b c <&3; ...done 3< <(echo $'1 2 3\n4 5 6')
(プロセス置換はbash/ksh/zsh機能です)
3。代わりにstderrからユーザー入力を取得します
または、逆に、例のようにパイプを使用しますが、ユーザーがread
の代わりにstderr
からstdin
を入力します。 :
_echo $'1 2 3\n4 5 6' |
while read a b c; do
read -u 2 -p "Enter a number: " d
echo "$a - $b - $c - $d ";
done
_
stderr
からの読み取りは少し奇妙ですが、実際には多くの場合、インタラクティブセッションで機能します。 (リダイレクトを実際にバイパスする場合は、_/dev/tty
_を明示的に開くこともできます。これは、データがパイプされている場合でも、less
がユーザーの入力を取得するために使用するものです。)
このようなstderr
を使用しても、すべてのケースで機能するわけではなく、read
の代わりに外部コマンドを使用している場合は、少なくとも一連のリダイレクトをコマンドに追加する必要があります。 。
また、_... | while
_に関するいくつかの問題については、 変数が1つの「while read」ループでローカルであるのに、別の見かけが同じループではローカルでないのはなぜですか? を参照してください。
4。必要に応じて配列の一部をスライスします
通常の1次元のスライスをコピーして、2Dっぽい配列を近似することもできます。
_data=(1 2 3
4 5 6)
n=3
for ((i=0; i < "${#data[@]}"; i += n)); do
a=( "${data[@]:i:n}" )
read -p "Enter a number: " d
echo "${a[0]} - ${a[1]} - ${a[2]} - $d "
done
_
変数の名前が必要な場合は、_${a[0]}
_などをa
、b
などに割り当てることもできますが、 Zshのほうがはるかに適切です 。
zsh
を使用すると、代わりに次のように記述できます。
for a b c (
1 2 3
4 5 6
'more complex' $'\n\n' '*** values ***'
) {
read 'd?Enter a number: '
do-something-with $a $b $c $d
}
2D配列については、ksh93
シェルも参照してください。
a=(
(1 2 3)
(4 5 6)
('more complex' $'\n\n' '*** values ***')
)
for i in "${!a[@]}"; do
read 'd?Enter a number: '
do-something-with "${a[i][0]}" "${a[i][1]}" "${a[i][2]}" "$d"
done
/dev/stdin
は1つだけあり、read
はそれが使用される場所(デフォルト)から読み取ります。
解決策は、1(/dev/stdin
)の代わりに他のファイル記述子を使用することです。
同等のコード(bash)から投稿したもの [1] (下を見て)0</dev/tty
(たとえば)を追加するだけで、「実際の」ttyから読み取ることができます。
while read a b c
do read -p "Enter a number: " d 0</dev/tty # 0<&2 is also valid
echo "$a -> $b -> $c and ++> $d"
done <<<"$(echo -e '1 2 3\n4 5 6')"
実行時:
$ ./script
Enter a number: 789
1 -> 2 -> 3 and ++> 789
Enter a number: 333
4 -> 5 -> 6 and ++> 333
他の代替手段は、0<&2
を使用することです(これは奇妙に見えるかもしれませんが、有効です)。
/dev/tty
(また0<&2
)からの読み取りは、スクリプトのstdinをバイパスすることに注意してください。これはエコーから値を読み取りません:
$ echo -e "33\n44" | ./script
1つの入力を他のfd(ファイル記述子)にリダイレクトする必要があります。
ksh、bash、zshで有効:
while read -u 7 a b c
do printf "Enter a number: "
read d
echo "$a -> $b -> $c and ++> $d"
done 7<<<"$(echo -e '1 2 3\n4 5 6')"
または、execを使用:
exec 7<<<"$(echo -e '1 2 3\n4 5 6')"
while read -u 7 a b c
do printf "Enter a number: "
read d
echo "$a -> $b -> $c and ++> $d"
done
exec 7>&-
Shで機能するソリューション(<<<
は機能しません):
exec 7<<-\_EOT_
1 2 3
4 5 6
_EOT_
while read a b c <&7
do printf "Enter a number: "
read d
echo "$a -> $b -> $c and ++> $d"
done
exec 7>&-
しかし、これはおそらく理解しやすいでしょう:
while read a b c 0<&7
do printf "Enter a number: "
read d
echo "$a -> $b -> $c and ++> $d"
done 7<<-\_EOT_
1 2 3
4 5 6
_EOT_
あなたのコードは:
echo -e "1 2 3\n4 5 6" |\
while read a b c;
do
echo "$a -> $b -> $c";
echo "Enter a number: ";
read d ;
echo "This number is $d" ;
done
簡略化されたコード(bash)は次のとおりです。
while read a b c
do #0</dev/tty
read -p "Enter a number: " d ;
echo "$a -> $b -> $c and ++> $d";
done <<<"$(echo -e '1 2 3\n4 5 6')"
実行すると、次のように表示されます。
$ ./script
1 -> 2 -> 3 and ++> 4 5 6
これは、var dが同じ/dev/stdin
から読み取られていることを示しています。