2017-03-27 1 views
0

제한된 bash 스크립팅 기술로 어떻게 해결할 수 있는지, 나는 어떻게 든 bash 스크립트에서 변수를 결합하고 싶다. 나는 다음과 같은 노력하고있어 :bash의 중첩 변수와 루프

#!/bin/bash 
basearch=x86_64 
ol7_channels="ol UEKR4 UEKR3" 
ol6_channels="ol UEKR4 UEKR3 UEK" 
ol5_channels="ol UEK" 

for version in 7 6 5 
do 
    for channel in ${ol${version}_channels}} 
    do 
    printf "Oracle Linux $version $channel $basearch" 
    done 
done 

바람직한 출력은 다음과 같습니다 난이 작동하지 않습니다했던 것처럼

OracleLinux 7 ol x86_64 

OracleLinux 7 UEKR4 x86_64 

OracleLinux 7 UEKR3 x86_64 

OracleLinux 6 ol x86_64 

OracleLinux 6 UEKR4 x86_64 

OracleLinux 6 UEKR3 x86_64 

OracleLinux 5 ol x86_64 

OracleLinux 5 UEKR4 x86_64 

OracleLinux 5 UEKR3 x86_64 

내가 변수 내부 변수를 넣어 이해합니다. 누구나이 방법을 보여줄 수 있습니까?

+0

귀하의 질문에 어디 하나의 배열이 없습니다. –

+1

그건 그렇고 - 어떤 버전의 bash? bash 4.3 이상에서이 작업을 수행하는 올바른 방법은 namevars를 사용하여 실제 배열을 가리키는 것입니다. –

+0

... btw, http://shellcheck.net/을 통해 코드를 실행하는 습관을 만드는 것을 고려하십시오. - 상수가 아닌 형식 문자열과 함께'printf'를 사용하면 나쁜 형식이라는 것을 지적 할 것입니다. (이것은 bash, C 및 C 언어와 같은 언어에서는 그렇지 않지만 "악성 폼"이 아니라 "악용 가능한 보안 버그의 원인"이라고도 함). –

답변

2

또 다른 방법 :

#!/bin/bash 

#bash 4.0+ 

basearch=x86_64 
declare -A channels=(
     [7]="ol UEKR4 UEKR3" 
     [6]="ol UEKR4 UEKR3 UEK" 
     [5]="ol UEK" 
) 

#or for version in 5 6 7 #if you need ordered 
for version in "${!channels[@]}" 
do 
     read -a chanlist <<<"${channels[$version]}" 
     for channel in "${chanlist[@]}" 
     do 
      echo "$version $channel $basearch" 
     done 
     #or replace the whole "for channel" loop with the following line 
     #printf "$version %s $basearch\n" "${chanlist[@]}" 
     #but read charles's comment 
done 

출력

7 ol x86_64 
7 UEKR4 x86_64 
7 UEKR3 x86_64 
6 ol x86_64 
6 UEKR4 x86_64 
6 UEKR3 x86_64 
6 UEK x86_64 
5 ol x86_64 
5 UEK x86_64 
+1

뛰어난. 누군가가 목록 요소를 분리하기 위해 공백을 원할 때 이것을 사용해야한다면 다른 문자를 사용하여'IFS'에'read'를 설정하면됩니다. 따라서, array + nameref 접근 방식과 거의 비슷한 유연성을 가지며 bash 4.0을 통해 호환 가능합니다. –

+0

... yeeeah, 그 특별한 변화 (non-constant format strings) 나는'version'이나'basearch' 값에' % 기호 또는 백 슬래시. –

+0

BTW,'$ {! channels [@]} "버전의 경우이 코드를 좀 더 DRY로 만들 수 있습니다. 버전 정의 목록을 하드 코딩하지 않아도됩니다. 주문에 대한 보증을 잃음). –

0

당신은 손 전에 변수를 구성하고 variable indirect reference

#!/bin/bash 
basearch=x86_64 
ol7_channels="ol UEKR4 UEKR3" 
ol6_channels="ol UEKR4 UEKR3 UEK" 
ol5_channels="ol UEK" 

for version in 7 6 5 
do 
    varname="ol${version}_channels"; 
    for channel in "${!varname}" 
    do 
    echo "Oracle Linux $version $channel $basearch" 
    done 
done 

중첩 된 변수 이름 do not work in bash을 사용할 수 있습니다.

+2

'$ {! varname}'은 "아마도"바람직하지 않지만 ** 강력하게 ** 바람직합니다. '버전'이 사용자가 제어 할 수있는 텍스트를 포함하고 있다면'eval' 버전으로 임의의 명령을 실행할 수 있습니다. –

+0

고마워요 !! 나는 eval을 제거하고 대신 $ {! varname}을 사용했다. "eval"과 같이 한 줄로 한 줄을 인쇄했기 때문에 : "Oracle Linux 7 eval x86_64" – Ichundu

+1

BTW, 어떻게 든 *를 독립 실행 형 단어로 ​​사용했다면 하나의 "배열"중 하나가 glob로 확장 된 것을 볼 수 있습니다 (현재 디렉토리의 파일 이름 목록으로 대체 됨). 마찬가지로, 공백이있는 채널 이름은이 형식으로 작동하지 않습니다 (반면 합법적 인 배열에서는 완전히 유효합니다). –

2

실제 배열은 실제 배열을 사용하며 (배열 인 것처럼 문자열을 처리하는 것과 반대) bash 4.3 기능 namerefs을 사용하여 해당 배열에 대한 별칭을 만듭니다.

#!/bin/bash 
basearch=x86_64 
channels_ol7=(ol UEKR4 UEKR3) 
channels_ol6=(ol UEKR4 UEKR3 UEK) 
channels_ol5=(ol UEK) 

for varname in "${[email protected]}"; do # iterate over variables starting with channels_ol 
    version=${varname#channels_ol}  # trim prefix to get version number 
    declare -n channels=$varname  # point channels nameref at our array 
    for channel in "${channels[@]}"; do # iterate over that array 
    printf 'Oracle Linux %s %s %s\n' "$version" "$channel" "$basearch" 
    done 
    unset -n channels     # clear the nameref before proceeding 
done 
+0

아쉽게도 CentOS 7 상자 (bash 버전은 4.2)에서는 작동하지 않습니다. "declare"에서 "-n"옵션을 인식하지 못합니다. 아치 리눅스를 실행중인 노트북에서 최신 버전의 bash를 사용하면 완벽하게 작동합니다. – Ichundu

+0

BTW, 조금 더 확장하여 '7', '6'및 '5'가 더 이상 필요하지 않게되었습니다. 그러나 버전 종속성은 여전히 ​​존재합니다. –