![Bash 배열의 간접 참조](https://linux55.com/image/40570/Bash%20%EB%B0%B0%EC%97%B4%EC%9D%98%20%EA%B0%84%EC%A0%91%20%EC%B0%B8%EC%A1%B0.png)
루프 없이 다음 스크립트를 작성할 수 있습니까?
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으로 평가됩니다.