"배열을 환경 변수로 사용한 다음 Bash 쉘 스크립트에서 호출할 수 있습니까?"라는 질문에 대한 답변을 찾아본 결과 "공식적으로" 지원되지 않는다는 결론을 내렸습니다(개요).여기, 그리고여기).
그러나 저는 이 작업을 꼭 수행해야 하며 "해결 방법"을 생각해 냈으므로 여러분의 의견을 듣고 싶습니다.
설정: 파일에 .profile
다음과 같은 변수를 만들었습니다.
HOST_NAMES='server1 server2 server3'
그런 다음 쉘 스크립트 시작 부분에 다음과 같이 썼습니다.
SRVR_ARRAY=($(echo $HOST_NAMES | awk '{for (i = 1; i <=NF; i++) print $i " "}'))
스크립트 후반부에서 일부 작업을 수행해야 할 때 다음을 호출했습니다.
for h in "${SRVR_ARRAY[@]}"; do
ping $h
done
효과가 있었어요! 이제 나는 이것이 Bash 쉘 스크립트 jerry-riggin의 최고의 버전이라고 확신합니다. 그러나 누군가가 이 사용법으로 어떤 위험을 볼 수 있는지 알고 싶었습니까?
답변1
직면할 수 있는 문제는 분할 및 와일드카드와 같은 일반적인 문제입니다.
문자열은 별도의 배열 항목으로 분할되고 전역 문자( *?[]
)와 유사한 모든 항목이 일치하는 파일 이름으로 확장되므로 공백을 포함할 수 없습니다. 호스트 이름은 문제가 아닐 수도 있지만 일반적으로 그렇습니다.
이것은 자주 묻는 질문이며 반복적으로 논의됩니다. 쉘 변수의 확장과 glob 및 분할의 영향 그리고 bash/POSIX 쉘에서 변수를 인용하는 것을 잊어버리는 보안 위험
IFS
이러한 문제를 해결하려면 a) 공백 이외의 항목을 설정하고 이를 사용하여 문자열을 구분하고 b) 를 비활성화해야 합니다 set -f
.
이는 |
구분 기호 역할을 하며 문자열을 분할합니다. 값에 원하지 않는 문자를 사용할 수 있습니다. \001
( $'\001'
Bash에서 이를 생성하는 데 사용되는)와 같은 일부 제어 문자도 사용할 수 있습니다.
#!/bin/bash
string='foo|bar'
IFS='|'; set -f
array=($string) # split it
IFS='|'
string="${array[*]}" # merge it back together
또한 주석에서 말했듯이 변수에 호스트 이름만 있고 공백으로 분할할 수 있는 경우 분할하기 위해 awk가 필요하지 않습니다. 쉘은 배열에 할당하거나 루프를 시작할 때 이 작업을 수행합니다.
SRVR_ARRAY=( $HOST_NAMES )
for x in $HOST_NAMES ; do ...
HOST_NAMES
(역시 전역 문자를 포함하면 문제가 발생합니다.)
실제로 이는 귀하의 예에서도 발생합니다. awk
무언가가 인쇄되고, 쉘이 이를 캡처하고, $()
큰따옴표 안에 있지 않기 때문에 이를 단어로 분할한 다음, 단어를 개별적으로 배열에 저장합니다.
답변2
declare -p
환경 변수에 출력을 저장할 수 있습니다 .
array=(foo 'bar baz'); array[12]=sparse
export ARRAY_definition="$(declare -p array)"
그런 다음 실행된 스크립트에서 다음을 수행합니다 bash
.
eval "$ARRAY_definition"
eval
와 동일한 로케일 declare
(바람직하게는 동일한 버전 bash
) 에서 실행하는 것이 중요합니다.
전역 범위에서 실행 되지 않으면 eval
배열이 선언됩니다.현지의.
참조를 사용하면 zsh
위험한 참조 사용을 피할 수 있습니다 eval
.
export ARRAY_definition=${(j: :)${(q)array}}
수입:
array=(${(Q)${(z)ARRAY_definition}})
rc
es
또는 이와 같은 것을 사용하거나 fish
기본적으로 배열 내보내기를 지원하는 셸(자체 인코딩 사용)을 사용할 수 있습니다.