web-dev-qa-db-ja.com

readによって駆動されるwhileループ内でプロンプトとしてreadを使用しますか?

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

私が探索する方法がわからない解決策への可能な道-

  1. 割り当てには、標準入力の代わりに別のファイルハンドルを使用します
  2. ... | 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
    
9
Debanjan Basu

これが正しければ、基本的に値のリストをループし、次にループ内で別の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]}_などをabなどに割り当てることもできますが、 Zshのほうがはるかに適切です

9
ilkkachu

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
2

/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_

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から読み取られていることを示しています。

2
Isaac