변수를 호출하는 데 따옴표로 묶인 명령 대체를 사용할 수 없습니다.

변수를 호출하는 데 따옴표로 묶인 명령 대체를 사용할 수 없습니다.

설명하다:

variable="Something that it holds"

그런 다음 echo "$variable"출력됩니다: 그것이 담고 있는 내용

하지만 나도 그렇다고 가정해 보세요.

var2="variable";  
echo "\$$(echo $var2)"

$variable
대신에: 무엇을 담고 있는지 만 출력합니다.

여기에서 Unix의 어떤 기능이 작동하는지 말해 줄 수 있는 사람이 있나요?

답변1

variable="Something"
var2="variable";
echo "\$$(echo $var2)"

마지막 줄에서는 "\$$(echo $var2)"$variable→ 을 예상합니다 Something. 이를 위해서는 쉘이 매개변수 확장을 두 번 수행해야 합니다. 그렇지는 않지만 단순히 $(echo $var2)접두사가 붙은 명령 대체 결과를 인쇄합니다 $.

원칙적으로,평가하다원하는 것을 얻을 수 있도록 도와주세요. 쉘이 첫 번째 단계 "\$$(echo $var2)"→ 를 실행한 후 $variableeval은 두 번째 단계 $variable→ 를 실행합니다 Something.

$ eval echo "\$$(echo $var2)"
Something

위 명령은 특정 사례에서는 괜찮지만 여전히 올바른 인용이 부족합니다.printf총애를 받다echo,

$ eval 'printf "%s\n" "${'"$var2"'}"'
Something

그러나 eval은 신뢰할 수 없는 데이터로 인해 보안 문제가 발생할 수 있습니다. 추정 var2="variable;rm importantFile". 이 경우 평가가 통과됩니다.

echo $variable;rm importantFile

쉘에 importantFile추가하면 행복하게 삭제됩니다.

일부 셸(예: Bash, ksh, Zsh)에서는 다음을 사용할 수도 있습니다.간접적인. Bash의 간접 확장 구문은 다음과 같습니다.

$ echo "${!var2}"
Something

var2="variable;rm importantFile"더 이상 문제가 아니지만 var2='a[$(rm importantFile)]'여전히 문제입니다.

간접 참조에 대해 자세히 알아보기배쉬 매뉴얼.

매개변수의 첫 번째 문자가 느낌표(!)이고 매개변수가 nameref가 아닌 경우 간접 참조 수준이 도입됩니다. Bash는 나머지 인수를 확장하여 형성된 값을 새 인수로 사용하고 해당 값은 원래 인수의 확장이 아닌 나머지 확장에 사용됩니다.

답변2

안티 패턴(*) 냄새가 납니다. 많은 쉘에는 문자열 인덱스 배열/사전이 있습니다. in ksh93(이 구문의 소스) bash또는 zsh:

typeset -A dictionary
x="keyX"
y="keyY"
dictionary[keyX]="valueX"
dictionary[$y]="valueY"

printf '%s\n' "${dictionary[$x]}"
printf '%s\n' "${dictionary[keyY]}"

(*) Linux 자체와는 아무런 관련이 없습니다. 변수에 있는 변수 이름은 매우 일반적인 변수입니다."안티패턴", 해서는 안 되는 일, 그리고 그것이 필요하다고 생각한다면 디자인에 뭔가 문제가 있는 것입니다(XY 문제)? 변수에서 변수 이름을 사용하는 대부분은 사전(존재하는 경우)으로 대체하는 것이 가장 좋습니다.

답변3

당신이 관찰하는 것은표준 행동중 하나POSIXshell: 일반적으로 말하면,

  1. 입력을 읽으십시오.

  2. 입력을 토큰(단어 및 연산자)으로 나눕니다.토큰 인식);

    • 이 단계에서 다음과 같은 문제가 발생할 때마다인용되지 않음 $(또는 `)은 확장 유형과 확장할 태그를 반복적으로 결정하여 필요한 모든 입력을 읽습니다.
  3. 입력을 단순 명령과 복합 명령으로 구문 분석합니다.

  4. 다양한 수행확장하다각 명령의 다른 부분에서 (각각) 명령 및 인수로 처리되는 경로 이름 및 필드 목록을 생성합니다.

  5. 리디렉션을 수행하고 매개변수 목록에서 리디렉션 연산자와 해당 피연산자를 제거합니다.

  6. 함수, 내장 실행 파일 또는 스크립트를 실행합니다.

  7. (선택 사항) 명령이 완료되고 종료 상태가 수집될 때까지 기다립니다.

구문 분석할 때 echo "\$$(echo $var2)"쉘은 두 가지 확장(2단계), 즉 큰따옴표로 묶인 명령 대체 $(echo $var2)와 따옴표가 없는 매개변수 확장을 감지합니다 $var2. 이스케이프된 $in은 \$큰따옴표가 달러 \기호 역할을 유지하므로 문자 그대로 달러 기호 로 처리됩니다.이스케이프 문자팔로우할 때 $.

이후 단계에서는 더 이상 확장된 감지가 발생하지 않습니다. 구체적으로, 4단계( "\$$(echo $var2)""\$$(echo variable)""\$variable"→ ) 에서 수행된 확장 결과는 $variable확장 트리거 문자를 감지하기 위해 더 이상 구문 분석되지 않습니다.

또한 이 $기호는 매개변수 확장의 맥락에서 변수 이름을 해당 내용으로 대체하는 데 사용되지만 범용으로 설계되지는 않았습니다.역참조 연산자.

존재하다표준 매개변수 확장, 가장 간단한 형태는 다음과 같습니다.${parameter}, 이것parameter사양에서는 변수 이름, 위치 매개변수 또는 특수 매개변수만 허용합니다(참조:"매개변수"의 정의). 엄밀히 말하면 매개변수 확장은 중첩될 수 없습니다. 확장 표현식은 다음과 같은 경우에만 허용됩니다.word다양한${parameter<symbols>[word]}형태).

다음을 사용하여 쉽게 확인할 수 있습니다.Z 쉘 제외${${foo}}은 유효한 표현식이 아니며, 셸 $$의 PID로 확장됩니다. 따라서 $foo의 값으로 확장되고 foo, $$foo셸의 PID와 리터럴 "foo"의 연결로 확장되고, $$$foo의 PID 연결로 확장됩니다. 쉘과 foo, ...) 의 값 .

답변4

간접 변수 확장 외에도 bash(버전 4.3부터)에서 다음을 사용할 수도 있습니다.이름 참조

declare -n var2="variable"  # "var2" is a _reference_ to "variable"

variable="Something"
echo "$var2"     # => Something

variable="something else"
echo "$var2"     # => something else

unset variable
echo "$var2"     # => ""

이것이 어떻게 구현되는지는 모르겠지만 흥미로운 정보가 있습니다. 간접 참조를 사용하면 nameref가 참조하는 내용을 찾을 수 있습니다.

echo ${!var2}    # => variable

관련 정보