各反復の開始時に複数の変数を読み取り、ループへのユーザー入力を読み取る必要があるユースケースがあります。
考えられる解決策を探る方法がわかりません。
- 割り当てには標準入力ではなく別のファイルハンドルを使用してください。
for
代わりにループを使用してください...ループ内に複数の変数を割り当てる... | while read ...
方法がわかりません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
答え1
read
私が正しくしている場合は、基本的に値のリストを繰り返してから、ループ内で別の値を繰り返したいと思います。
ここにはいくつかのオプションがあります。 1と2はおそらく最も賢いでしょう。
1. 文字列を使用した配列シミュレーション
2D配列を持つことは良いですが、Bashでは不可能です。値にスペースがない場合のおおよその回避策は、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. パイプを無料の標準入力にリダイレクトする別のパイプと交換します。
もう1つの可能性は、別のfdから読み込み、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からユーザー入力を受け取る
または、代わりに例のようなパイプを使用しますが、ユーザー入力がパイプソースの代わりに(fd 2)から出るように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
少なくともそのコマンドに複数のリダイレクトを追加する必要があります。
また見てください私の変数が1つの「読み込み中」ループではローカルですが、一見似ている他のループではローカルではないのはなぜですか?いくつかの関連する質問があります... | while
。
4. 必要に応じてアレイの一部をスライスします。
通常の1D配列のスライスをコピーして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
変数の名前が必要な場合は、etc${a[0]}
になどを割り当てることもできますがa
、b
Zshはもっとうまくいくでしょう。。
答え2
は1つだけで、/dev/stdin
使用read
されているすべての場所で読み取られます(デフォルトでは)。
解決策は、1()の代わりに別のファイル記述子を使用することです/dev/stdin
。
同等のコード(bash)から投稿した内容まで[1](下の表示)「実際の」ttyから読むには(たとえば)
追加するだけです。0</dev/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
)はスクリプトの標準入力をバイパスするため、echoから値を読み取ることはありません。
$ echo -e "33\n44" | ./script
その他のソリューション
必要なのは、ある入力を別の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_
簡単なコード1つ
あなたのコードは次のとおりです
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
。
答え3
を使用するには、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