루프에서 여러 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

배열의 두 번째와 세 번째 요소를 인쇄하고 싶지만 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는 or array로만 사용됩니다 .AB

if설명에서는 루프의 세 번째 배열 요소를 분할하기 위해 셸을 사용하지 않습니다. 대신, 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각 값을 하나씩 설정 하므로 ${A[0]}첫 번째 반복을 수행한 다음 ${B[0]}두 번째 반복을 수행합니다.

${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보다 더 나은 데이터 구조 지원을 제공합니다.

관련 정보