~에서배쉬 매뉴얼:
동일한 이름을 가진 쉘 함수 및 변수는 쉘 하위 항목에 전달되는 동일한 이름을 가진 환경에 여러 항목이 생성될 수 있습니다. 이로 인해 문제가 발생할 수 있으니 주의하세요.
bash는 "쉘 함수와 동일한 이름을 가진 변수"를 어떻게 구별합니까?
$ func () { return 3; }; func=4; declare -p func; declare -f func;
declare -- func="4"
func ()
{
return 3
}
"환경에서 동일한 이름을 가진 여러 항목이 쉘의 하위 항목으로 전달"되는 경우는 언제입니까?
문제가 발생할 때 무엇에 "주의"해야 합니까?
답변1
전반적인 상황: 독립적인 네임스페이스
일반적으로 쉘은 변수와 함수가 서로 다른 컨텍스트에서 사용되기 때문에 이를 구별합니다. 간단히 말해서 이름은 a 뒤에 나타나 $
거나 내장 함수에 대한 인수로 나타나는 경우(예: export
(without -f
) 및 unset
(without )) 변수 이름입니다. -f
이름은 명령(별명 확장 후 ) 또는 인수 등으로 나타나는 경우 함수 export -f
이름 입니다.unset -f
변수를 환경으로 내보낼 수 있습니다. 환경 변수는 셸 변수와 동일한 이름(및 동일한 값)을 갖습니다.
이전 bash의 경우: 함수 내보내기로 인한 혼란
대부분의 다른 셸과 달리 Bash는 기능을 환경으로 내보낼 수도 있습니다. 환경에는 유형 표시가 없기 때문에 환경의 항목이 환경 변수의 이름이나 값을 분석하는 것 외에는 기능인지 여부를 식별할 수 있는 방법이 없습니다.
이전 버전의 bash는 함수 이름을 이름으로 사용하고 함수 정의와 비슷한 것을 함수 값으로 사용하여 환경에 함수를 저장했습니다. 예를 들어:
bash-4.1$ foobar () { echo foobar; }
bash-4.1$ export -f foobar
bash-4.1$ env |grep -A1 foobar
foobar=() { echo foobar
}
bash-4.1$
{ echo foobar; }
코드가 다음과 같은 함수 와 값이 다음과 같은 변수 () { echo foobar}
(여기서는 
개행 문자임) 사이에는 차이가 없습니다 . 이는 잘못된 디자인 결정으로 판명되었습니다.
때로는 잠재적으로 적대적인 개체에 의해 값이 제어되는 환경 변수를 사용하여 쉘 스크립트가 호출됩니다. 예를 들어 CGI 스크립트입니다. Bash의 함수 내보내기/가져오기 기능을 사용하면 이러한 방식으로 함수를 주입할 수 있습니다. 예를 들어 스크립트를 실행합니다.
#!/bin/bash
ls
환경에 특정 이름(예: )을 가진 변수가 포함되지 않는 한 PATH
원격 요청의 요청은 안전합니다 . 그러나 요청이 환경 변수를 ls
로 설정할 수 있다면 그것이 함수의 본문이므로 () { cat /etc/passwd; }
bash는 이를 기꺼이 실행할 것입니다 .cat /etc/passwd
ls
최신 bash 사용: 훨씬 덜 혼란스럽습니다.
이 보안 취약점은 다음으로 인해 발생합니다.스티븐 차제라스한 측면으로쉘 쇼크 버그. Shellshock 이후 버전의 bash에서 내보낸 함수는 다음으로 식별됩니다.이름내용보다는.
bash-4.3$ foobar () { echo foobar; }
bash-4.3$ export -f foobar
bash-4.3$ env |grep -A1 foobar
BASH_FUNC_foobar%%=() { echo foobar
}
BASH_FUNC_foobar%%
유사한 이름은 일반적으로 명령 이름으로 사용되지 않으며 환경 변수 전달을 허용하는 인터페이스를 통해 필터링할 수 있으므로 보안 문제는 없습니다 . 기술적으로 %
환경 변수 이름에 문자를 포함하는 것이 가능합니다(이것이 현대 bash의 내보낸 함수가 작동하는 이유입니다). 그러나 일반적으로 쉘은 %
변수 이름을 허용하지 않기 때문에 사람들은 그렇게 하지 않습니다.
bash 매뉴얼의 이 문장은 이전(Shellshock 이전) 동작을 나타냅니다. 업데이트하거나 삭제해야 합니다. 최신 bash 버전의 경우 환경 변수 이름이 %%
.
답변2
Emacs Lisp에서도 비슷한 상황이 발생합니다. 여기에는 두 개의 네임스페이스가 있는데, 하나는 함수용이고 다른 하나는 변수용입니다. (var)
함수 컨텍스트()에서 기호를 역참조하면 함수가 호출되고, 변수 컨텍스트( 예 var
: 괄호 없이)에서 기호를 역참조하면 변수가 제공됩니다. 예를 들어:
(defun myvar (myvar)
"adds 3 to MYVAR"
(+ 3 myvar))
(setq myvar 7)
(message (myvar myvar))
실행됩니다기능 myvar
매개변수는 7
변수의 역참조입니다 myvar
.
익숙하지 않으면 매우 혼란스러울 수 있습니다.
문제를 검토하고 테스트한 후세게 때리다동일한 동작을 보이는 것에 놀랐습니다. 위의 ELisp를 bash로 변환합니다.
[grochmal@phoenix ~]$ myvar () { echo $(($1+3)); }
[grochmal@phoenix ~]$ myvar=7
[grochmal@phoenix ~]$ myvar $myvar
10
$
이 점에서 Bash는 변수에 레이블을 지정 해야 하기 때문에 ELisp보다 더 혼란스럽습니다 . 그래도 이 이름은 declare
두 가지를 포괄하는 이름처럼 보일 수도 있습니다. 바라보다:
[grochmal@phoenix ~]$ declare -p myvar
declare -x myvar="7"
[grochmal@phoenix ~]$ declare -f myvar
myvar ()
{
echo $(($1+3))
}
(PS 일단 두 개의 네임스페이스의 존재에 익숙해지면, 예를 들어 한동안 ELisp에서 프로그래밍을 했다면 더 이상 혼란스럽지 않습니다.)