연관 배열 외에도 Bash에서 동적 변수를 달성하는 몇 가지 방법이 있습니다. 이 모든 기술은이 답변의 끝에서 논의되는 위험을 나타냅니다.
다음 예제에서는 초기 값이 i=37
이라는 변수에 별칭을 지정 한다고 가정합니다 .var_37
lolilol
방법 1. "포인터"변수 사용
C 포인터와 달리 변수 이름을 간접 변수에 간단히 저장할 수 있습니다. 배시는 신택스 갖는 판독 에일리어싱 변수 : ${!name}
이름 변수의 값이 변수의 값으로 확장된다 name
. 이를 2 단계 확장으로 생각할 수 있습니다.으로 ${!name}
확장하면 $var_37
으로 확장됩니다 lolilol
.
name="var_$i"
echo "$name" # outputs “var_37”
echo "${!name}" # outputs “lolilol”
echo "${!name%lol}" # outputs “loli”
# etc.
불행하게도, 앨리어싱 된 변수 를 수정 하기 위한 구문이 없습니다 . 대신 다음 트릭 중 하나를 사용하여 과제를 수행 할 수 있습니다.
1a. 로 할당eval
eval
사악하지만, 우리 목표를 달성하는 가장 단순하고 휴대하기 쉬운 방법이기도합니다. 과제가 두 번 평가되므로 과제의 오른쪽을 조심스럽게 탈출해야합니다 . 이 작업을 수행하는 쉽고 체계적인 방법은 오른쪽을 미리 평가하거나 사용하는 것 printf %q
입니다.
그리고 왼쪽이 유효한 변수 이름인지 또는 색인이있는 이름인지 (? 인 경우) 수동으로 확인해야합니다 evil_code #
. 대조적으로, 아래의 다른 모든 방법은 자동으로 적용됩니다.
# check that name is a valid variable name:
# note: this code does not support variable_name[index]
shopt -s globasciiranges
[[ "$name" == [a-zA-Z_]*([a-zA-Z_0-9]) ]] || exit
value='babibab'
eval "$name"='$value' # carefully escape the right-hand side!
echo "$var_37" # outputs “babibab”
단점 :
- 변수 이름의 유효성을 검사하지 않습니다.
eval
악하다.
eval
악하다.
eval
악하다.
1b. 로 할당read
read
내장 당신이 이름, 여기 - 문자열과 함께 이용 될 수있는 사실을주고있는 변수에 값을 할당 할 수 있습니다 :
IFS= read -r -d '' "$name" <<< 'babibab'
echo "$var_37" # outputs “babibab\n”
IFS
부분과 옵션은 -r
그대로 옵션 동안 값이 할당되어 있는지 확인합니다 -d ''
여러 줄의 값을 할당 할 수 있습니다. 이 마지막 옵션으로 인해 명령은 0이 아닌 종료 코드와 함께 리턴됩니다.
here-string을 사용 하므로 개행 문자가 값에 추가됩니다.
단점 :
- 다소 모호한;
- 0이 아닌 종료 코드로 리턴합니다.
- 값에 개행을 추가합니다.
1c. 로 할당printf
Bash 3.1 (2005 릴리스) 이후로 printf
내장 기능은 이름이 지정된 변수에 결과를 할당 할 수도 있습니다. 이전 솔루션과 달리, 작동하기 때문에, 물건을 피하기 위해 추가적인 노력이 필요하지 않으며, 분할 등을 방지 할 수 있습니다.
printf -v "$name" '%s' 'babibab'
echo "$var_37" # outputs “babibab”
단점 :
방법 2. "참조"변수 사용
Bash 4.3 (2014 릴리스) 이후로 declare
내장 기능에는 -n
C ++ 참조와 같이 다른 변수에 대한 "이름 참조"인 변수를 작성 하는 옵션 이 있습니다. 방법 1에서와 같이 참조는 앨리어싱 된 변수의 이름을 저장하지만 참조에 액세스 할 때마다 (읽기 또는 할당을 위해) Bash는 자동으로 간접 지시를 해결합니다.
또한 Bash에는 참조 자체의 가치를 얻는 데있어 특별하고 매우 혼란스러운 구문이 ${!ref}
있습니다.
declare -n ref="var_$i"
echo "${!ref}" # outputs “var_37”
echo "$ref" # outputs “lolilol”
ref='babibab'
echo "$var_37" # outputs “babibab”
이것은 아래에 설명 된 함정을 피하지 않지만 적어도 구문을 간단하게 만듭니다.
단점 :
위험
이러한 모든 앨리어싱 기술은 몇 가지 위험을 초래합니다. 첫 번째 는 간접 지시를 읽을 때마다 (읽거나 할당하기 위해) 임의의 코드를 실행하는 것 입니다. 실제로와 같은 스칼라 변수 이름 대신과 같은 var_37
배열 첨자를 별칭으로 지정할 수도 있습니다 arr[42]
. 그러나 Bash는 필요할 때마다 대괄호의 내용을 평가하므로 앨리어싱 arr[$(do_evil)]
은 예기치 않은 영향을 미칩니다. 따라서 앨리어스의 출처를 제어 할 때만이 기술을 사용하십시오 .
function guillemots() {
declare -n var="$1"
var="«${var}»"
}
arr=( aaa bbb ccc )
guillemots 'arr[1]' # modifies the second cell of the array, as expected
guillemots 'arr[$(date>>date.out)1]' # writes twice into date.out
# (once when expanding var, once when assigning to it)
두 번째 위험은 주기적 별명을 작성하는 것입니다. Bash 변수는 해당 범위가 아닌 이름으로 식별되므로 실수로 별칭을 만들 수 있습니다. 이는 공통 변수 이름 (과 같은 var
)을 사용할 때 특히 발생할 수 있습니다 . 결과적으로 별명 변수의 이름을 제어 할 때만이 기술을 사용하십시오 .
function guillemots() {
# var is intended to be local to the function,
# aliasing a variable which comes from outside
declare -n var="$1"
var="«${var}»"
}
var='lolilol'
guillemots var # Bash warnings: “var: circular name reference”
echo "$var" # outputs anything!
출처: