변수 확장을 연기하는 방법


18

스크립트 상단에 아직 설정되지 않은 변수를 사용하여 일부 문자열을 초기화하려고했습니다.

str1='I went to ${PLACE} and saw ${EVENT}'
str2='If you do ${ACTION} you will ${RESULT}'

그리고 나중에 PLACE, EVENT, ACTION, 및이 RESULT설정됩니다. 그런 다음 변수를 확장하여 문자열을 인쇄 할 수 있기를 원합니다. 내 유일한 옵션 eval입니까? 이것은 작동하는 것 같습니다 :

eval "echo ${str1}"

이 표준입니까? 더 좋은 방법이 있습니까? eval변수가 무엇이든 고려하여 실행하지 않는 것이 좋습니다 .

답변:


23

표시되는 종류의 입력에서 쉘 확장을 활용하여 값을 문자열로 대체하는 유일한 방법 eval은 어떤 형식 으로 사용 하는 것입니다. 이는 값을 제어하고 str1안전하다고 알려진 변수 (기밀 데이터를 포함하지 않음) 만 인용하고 인용되지 않은 다른 쉘 특수 문자를 포함하지 않는 한 안전합니다. 큰 따옴표 또는 here 문서에서 문자열을 확장해야합니다.이 방법 "$\`은 특별합니다 (앞에 \in이 있어야 함 str1).

eval "substituted=\"$str1\""

문자열 대신 함수를 정의하는 것이 훨씬 강력합니다.

fill_template () {
  sentence1="I went to ${PLACE} and saw ${EVENT}"
  sentence2="If you do ${ACTION} you will ${RESULT}"
}

변수를 설정 한 다음 함수 fill_template를 호출 하여 출력 변수를 설정하십시오.

PLACE=Sydney; EVENT=fireworks
ACTION='not learn from history'; RESULT='have to relive history'
fill_template
echo "During my holidays, $sentence1."
echo "Cicero said: \"$sentence2\"."

2
평가를 지연시키고 명시적인 평가 호출을 피하는 기능을 사용하는 것이 좋습니다.
Clayton Stanley

좋은 해결책, 이것은 많은 도움이되었습니다. 감사!
스튜어트

8

나는 당신의 의미를 취할 때, 이러한 답변 중 어느 것이 맞는지 믿지 않습니다. eval어떤 식으로도 필요하지 않으며 변수를 두 번 평가해야 할 필요도 없습니다.

사실 @Gilles는 매우 가까워 지지만 값을 무시하는 문제와 값이 두 번 이상 필요한 경우 어떻게 사용해야하는지 다루지 않습니다. 결국 템플릿을 두 번 이상 사용해야합니까?

나는 그것이 당신이 그들을 평가하는 순서가 중요하다고 생각합니다. 다음을 고려하세요:

상단

여기에 몇 가지 기본값을 설정하고 호출 될 때 인쇄 할 준비를합니다.

#!/bin/sh
    _top_of_script_pr() ( 
        IFS="$nl" ; set -f #only split at newlines and don't expand paths
        printf %s\\n ${strings}
    ) 3<<-TEMPLATES
        ${nl=
}
        ${PLACE:="your mother's house"}
        ${EVENT:="the unspeakable."}
        ${ACTION:="heroin"}
        ${RESULT:="succeed."}
        ${strings:="
            I went to ${PLACE} and saw ${EVENT}
            If you do ${ACTION} you will ${RESULT}
        "}
    #END
    TEMPLATES

가운데

여기에서 결과에 따라 인쇄 기능을 호출 할 다른 기능을 정의합니다.

    EVENT="Disney on Ice."
    _more_important_function() { #...some logic...
        [ $((1+one)) -ne 2 ] && ACTION="remedial mathematics"
            _top_of_script_pr
    }
    _less_important_function() { #...more logic...
        one=2
        : "${ACTION:="calligraphy"}"
        _top_of_script_pr
    }

바닥

이제 모든 설정이 완료되었으므로 여기에서 결과를 실행하고 가져올 수 있습니다.

    _less_important_function
    : "${PLACE:="the cemetery"}" 
    _more_important_function
    : "${RESULT:="regret it."}" 
    _less_important_function    

결과

잠시 이유를 살펴 보 겠지만 위의 내용을 실행하면 다음과 같은 결과가 나타납니다.

_less_important_function()'s 첫 실행 :

나는 당신의 어머니의 집에 가서 Disney on Ice를 보았습니다 .

당신이 경우에 서예를 당신은 성공합니다.

그때 _more_important_function():

나는 묘지에 가서 Disney on Ice를 보았습니다.

당신이 경우에 치료하는 수학을 당신은 성공합니다.

_less_important_function() 다시:

나는 묘지에 가서 Disney on Ice를 보았습니다.

교정 수학을하면 후회하게됩니다.

작동 방식 :

여기서 핵심 기능은 다음과 같은 개념입니다 conditional ${parameter} expansion.. 변수를 설정하지 않거나 null 형식으로 만 변수를 값으로 설정할 수 있습니다.

${var_name: =desired_value}

대신 설정되지 않은 변수 만 설정하려는 경우 :colon및 null 값은 그대로 유지됩니다.

범위 :

당신은 위의 예제에서 알 수 있습니다 $PLACE$RESULT를 통해 설정할 때 바뀌지 parameter expansion하더라도 _top_of_script_pr()이미 호출 된 아마 그 그것의 실행 설정. 이 작품은 즉 그 이유는 _top_of_script_pr()A는 ( subshelled )기능 - 나는 그것을 둘러싸 parens댄 아니라 { curly braces }다른 사람을 위해 사용. 서브 쉘에서 호출되기 때문에 설정하는 모든 변수 locally scoped는 상위 쉘로 돌아가고 해당 값이 사라집니다.

그러나 _more_important_function()설정 하면 설정 $ACTIONglobally scoped다음을 통해서만 설정 되므로 _less_important_function()'s두 번째 평가에 영향을 미칩니다.$ACTION_less_important_function()$ACTION${parameter:=expansion}.

:없는

그리고 나는 최고의 사용 왜 :colon?음을의 man페이지가 당신을 말할 것이다 : does nothing, gracefully.당신은 참조 parameter expansion를 - 그것은 같은 소리 정확히 expands의 값에 ${parameter}.따라서 함께 우리가 변수를 설정할 때 ${parameter:=expansion}우리는 그 값으로 남겨 - 어떤 쉘 의지 인라인 실행을 시도하십시오. 실행하려고하면 the cemetery오류가 발생합니다. PLACE="${PLACE:="the cemetery"}"동일한 결과를 생성하지만이 경우에도 중복되며 쉘을 선호합니다.: ${did:=nothing, gracefully}.

그것은 당신이 이것을 할 수있게합니다 :

    echo ${var:=something or other}
    echo $var
something or other
something or other

여기 문서

그리고 null 또는 unset 변수의 인라인 정의도 다음과 같은 이유입니다.

    <<HEREDOC echo $yo
        ${yo=yoyo}
    HEREDOC
yoyo

를 생각하는 가장 좋은 방법 here-document 은 입력 파일 디스크립터로 스트리밍되는 실제 파일입니다. 다소간 그것이 그 자체이지만, 다른 쉘은 약간 다르게 구현합니다.

어쨌든, 인용하지 않으면 <<LIMITER스트리밍 되고 평가됩니다. expansion.따라서 here-document캔에 변수를 선언하면 작동하지만 expansion아직 설정되지 않은 변수 만 설정하도록 제한합니다. 그러나 템플릿 인쇄 기능을 호출 할 때 항상 기본값이 설정되므로 설명한대로 요구 사항에 완벽하게 맞습니다.

왜 안돼 eval?

제가 제시 한 예는 안전하고 효과적인 수락 방법을 제공 parameters.합니다. 범위 를 다루기 때문에 set via 내의 모든 변수 ${parameter:=expansion}는 외부에서 정의 할 수 있습니다. 따라서이 모든 것을 template_pr.sh라는 스크립트에 넣고 실행하면 :

 % RESULT=something_else template_pr.sh

당신은 얻을 것이다 :

네 엄마 집에 가서 Disney on Ice를 봤어

당신이 서예를하면 당신은 something_else

나는 묘지에 가서 Disney on Ice를 보았습니다.

교정 수학을하는 경우에는 something_else

나는 묘지에 가서 Disney on Ice를 보았습니다.

교정 수학을하는 경우에는 something_else

이 같은 문자 그대로 스크립트에서 설정 한 해당 변수에 대한 작동하지 않을 것입니다 $EVENT, $ACTION,그리고 $one,난 단지 차이를 보여주기 위해 그런 식으로 사람들을 정의하지만.

어쨌든, evaled명령문 에 알 수없는 입력을 받아들이는 것은 본질적으로 안전하지 않지만, parameter expansion특별히 그렇게하도록 설계되었습니다.


1

확장되지 않은 변수 대신 문자열 템플릿에 자리 표시자를 사용할 수 있습니다. 이것은 매우 지저분해질 것입니다. 당신이하고있는 일이 템플릿이 많은 경우 실제 템플릿 라이브러리가있는 언어를 고려할 수 있습니다.

format_template() {
    changed_str=$1

    for word in $changed_str; do
        if [[ $word == %*% ]]; then
            var="${word//\%/}"
            changed_str="${changed_str//$word/${!var}}"
        fi
    done
}

str1='I went to %PLACE% and saw %EVENT%'
PLACE="foo"
EVENT="bar"
format_template "$str1"
echo "$changed_str"

위의 단점은 템플릿 변수가 자신의 단어 여야한다는 것입니다 (예 : 할 수 없음 "%prefix%foo"). 이것은 약간의 수정으로 수정되거나 템플릿 변수가 동적이 아닌 하드 코딩으로 간단하게 수정 될 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.