ループ内で複数のBash配列を繰り返す際に発生する問題

ループ内で複数のBash配列を繰り返す際に発生する問題

Bashの配列にはいくつかの問題があります。

A=( "127.0.0.1" "localhost" "aaa nnn cvcc" )
B=( "8.8.8.8"  "dns" "bbb tttt rrrr")

for n in ${A} ${B} ;  do

 if ping -c3 ${n[0]};then
  echo "${n[1]}"
    for share in ${n[2]};do
      echo $share
    done
 fi
done

配列の2番目と3番目の要素を印刷したいのですが、forループはpingで停止します。だから動作します。

if ping -c3 ${A[0]};then
 echo "${A[1]}"
 for share in ${A[2]};do
  echo $share
 done
fi

本当に愚かなことですが、本当に狂っていると思います...アイデアがありますか?事前にありがとう

答え1

Aループは実際にはandの最初の要素であるandを繰り返しますB。これは${A[0]}、andがwhileと同じ空の文字列であることを${B[0]}意味します。${n[1]}${n[2]}${n[0]}$n

bashバージョン4.3以降では、名前参照変数を使用してこれを実行できます。

#!/bin/bash

A=( 127.0.0.1 localhost alpha beta gamma theta )
B=( 8.8.8.8   dns       itsy bitsy 'spider man' )

for variable in A B; do
        declare -n array="$variable"

        if ping -c3 "${array[0]}" >/dev/null; then
                printf '%s\n' "${array[1]}"
                printf '\t%s\n' "${array[@]:2}"
        fi
done

変数を繰り返しています。名前 AそしてB、その内容よりは。ループ内でarrayという変数の名前参照を作成します$variable。 Nowはorarrayでのみ使用されます。AB

このif説明では、ループの3番目の配列要素を分割するためにシェルを使用しません。代わりに、A配列を作成するときにB手動で分割し、配列printfのオフセットを使用して単一の呼び出しを実行して要素を出力しますarray。読みやすくするために、各要素の前にタブを出力しました。

localhost
        alpha
        beta
        gamma
        theta
dns
        itsy
        bitsy
        spider man

このifステートメントは次のように書くこともできます。

if ping -c3 "${array[0]}" >/dev/null; then
        printf '%s: ' "${array[1]}"
        ( IFS=,; printf '%s\n' "${array[*]:2}" )
fi

残りの配列要素をコロンの後にカンマ区切りのリストとして出力します。

localhost: alpha,beta,gamma,theta
dns: itsy,bitsy,spider man

注:上記の引用は偶然ではありません。参照配列を拡張すると、各要素が個別に参照されます。個々の要素を引用する拡張は、シェルが空白に値を分割しないことを保証します(そして分割された単語に対してファイル名のグロービングを実行しません)。

また、見ることができます


/bin/sh名前付き配列を使用せずにこれを実行することもできます。代わりに、位置引数リストを使用してデータを保持し、リストの各部分を特別な単語(END選択したランダムな文字列であるを使用)で終了します。次にリストを繰り返し、リストから要素を削除します。

#!/bin/sh

set --  127.0.0.1 localhost alpha beta gamma theta END \
        8.8.8.8   dns       itsy bitsy 'spider man' END

while [ "$#" -gt 0 ]; do
        if ping -c 3 "$1" >/dev/null; then
                printf '%s\n' "$2"
                shift 2
                while [ "$1" != END ]; do
                        printf '\t%s\n' "$1"
                        shift
                done
        else
                while [ "$1" != END ]; do shift; done
        fi

        shift
done

答え2

この回答はあなたの問題を直接解決するものではありませんが、問題に対する代替ソリューションを提供します。配列に依存しません。

配列を扱う代わりにファイルを使用します。私の考えでは、ファイルを使用するとプログラムからデータを分離できるという利点があります。作成とメンテナンスが簡単です。

これを行うには、「共有」をカンマ,または別の文字で区切ります。

ファイル.txt

127.0.0.1 localhost aaa,nnn,cvcc
8.8.8.8  dns bbb,tttt,rrrr
failedip  failed a,b,c

スクリプト:

#!/bin/bash

# loop over the file's lines
while IFS=' ' read -r ip domain share; do
  # a blank line to separate results at each loop
  echo
  # perform the `ping`, redirect to `/dev/null` if you don't need to print the output
  if ping -c3 "$ip" &> /dev/null;then
    echo "ping to $ip successful"
    echo "domain: $domain"
    # change the IFS to comma ',' in a subshell to parse the 'shares'
    (IFS=,; printf "share: %s\n" $share)
  else
    # message if `ping` failed
    echo "ping to $ip failed"
  fi
done < file.txt

出力:


ping to 127.0.0.1 successful
domain: localhost
share: aaa
share: nnn
share: cvcc

ping to 8.8.8.8 successful
domain: dns
share: bbb
share: tttt
share: rrrr

ping to failedip failed

答え3

for n in ${A} ${B} ;  do

これはあなたが望むことをしません。インデックスなしで配列を取得することは、インデックス0の要素を取得するのと${A}同じ${A[0]}ですB。ループは$n各値を1つずつ設定するため、最初の${A[0]}反復を実行してから${B[0]}2回目の反復を実行します。

${n[0]}配列ではないなどにも同様に適用されるので、${n[1]}そこには何もなく、他のすべては設定されていないだけです。${n[2]}$n${n[0]}

Bashは実際に2D配列を処理しませんが、次のように構造を反転できます。

ips=(127.0.0.1 8.8.8.8)
names=(localhost dns)
for i in "${!ips[@]}"; do
   if ping -c3 "${ips[i]}"; then
       echo "host ${names[i]-"(unknown")} is up"
   fi
done

ただし、とにかく別のファイルにデータを保存する必要があります。


Zshでは、データ構造とループに関連するすべてが異なる可能性があり、IIRC kshもBashよりも優れたデータ構造サポートを提供します。

関連情報