Bash 배열의 간접 참조

Bash 배열의 간접 참조

루프 없이 다음 스크립트를 작성할 수 있습니까?

IPv4_first=1.1.1.1
IPv4_second=2.2.2.2
IPv4_third=3.3.3.3

IPv4_all=() 

for var in ${!IPv4_@}
do
   IPv4_all+=(${!var})
done

printf "'%s'\n" "${IPv4_all[@]}"

그것은 다음과 같습니다:

IPv4_all=${!${!IPv4_@}}

답변1

이것은 아마도 내가 작성한 것 중 가장 추악한 Bash 코드일 것입니다. 하지만...

IPv4_first=1.1.1.1
IPv4_second=2.2.2.2
IPv4_third=3.3.3.3

names=(${!IPv4_@})
eval "IPv4_all=(${names[@]/#/$})"
printf "'%s'\n" "${IPv4_all[@]}"

보세요, 엄마, 루프가 없어요!

${names[@]/#/$}접두사 $각 요소의 시작 부분에 고정된 빈 문자열을 일치시켜 배열의 각 요소 시작 부분에 연결합니다. 이는 eval배열 이니셜라이저 내에서 변수 참조를 얻기 위해 확장할 수 있는 변수 역참조 배열을 제공합니다 . 여러 매개변수 확장을 동시에 적용할 수 없기 때문에 두 개의 별도 라인이 필요합니다.

출력은 다음과 같습니다

'1.1.1.1'
'2.2.2.2'
'3.3.3.3'

예상대로.

이 줄은 다음으로 대체될 수 있습니다.

IPv4_all=($(eval "echo ${names[@]/#/$}"))

eval링 배열 할당 대신 . 이것이 더 나은지 잘 모르겠습니다.

변수 값에 공백이나 기타 IFS 문자가 포함될 수 있는 경우 eval을 변경할 수 있습니다.

eval "IPv4_all=($(printf '"$%s" ' "${names[@]}"))"

이는 모든 변수를 큰따옴표로 올바르게 인용합니다.

답변2

변수가 필요하지 않습니다 IPv4_all.

eval printf "\'%s\'\\\n" $(printf "$%s\n" ${!IPv4_@})

산출:

'1.1.1.1'
'2.2.2.2'
'3.3.3.3'

답변3

내 콘테스트 항목은 가장 추악하고 가장 복잡한 bash 코드에 대한 것입니다 ;-):

eval 'declare(){ v=${2%%=*};[[ $v = IPv4_* ]]&&IPv4_all+=("${!v}");};'"$(declare -p)"
unset -f declare

답변4

나는 이것이 나에게 효과가 있다고 생각하지만 몇 가지 기본 사항이 누락되었을 수 있음을 인정합니다.

IPv4_first=1.1.1.1
IPv4_second=2.2.2.2
IPv4_third=3.3.3.3

IPv4_all=( $(set | sed '/IPv4_.*[=)]/!d;s///') )

printf "'%s'\n" "${IPv4_all[@]}"

산출

'1.1.1.1'
'2.2.2.2'
'3.3.3.3'

이것이 더 좋습니다:

eval IPv4_all=( "$(set |
    grep -E '^IPv4_[_[:alnum:]]*=([^(]|$)' |
    sed 's/\([^=]*\).*/${\1+"$\1"} /')"
)

grep대상 변수와 일치하는 안전한 행만 가져옵니다. sed매개변수 확장 표시로 둘러싸서 실제로 현재 쉘 변수 이름이 아닌 경우 0으로 평가됩니다.

관련 정보