読み取りで駆動されるwhileループで読み取りをヒントとして使用しますか?

読み取りで駆動されるwhileループで読み取りをヒントとして使用しますか?

各反復の開始時に複数の変数を読み取り、ループへのユーザー入力を読み取る必要があるユースケースがあります。

考えられる解決策を探る方法がわかりません。

  1. 割り当てには標準入力ではなく別のファイルハンドルを使用してください。
  2. 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します。stderrstdin

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]}になどを割り当てることもできますがabZshはもっとうまくいくでしょう。

答え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

関連情報