다음과 같이 아직 설정되지 않은 변수를 사용하여 스크립트 상단의 일부 문자열을 초기화하고 싶습니다.
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
변수가 무엇이든 될 수 있다는 점을 고려하면 실행하지 않는 것이 가장 좋습니다.
답변1
표시되는 입력 유형의 경우 셸 확장을 활용하여 값을 문자열로 바꾸는 유일한 방법은 eval
. 이는 값을 제어 str1
하고 안전한 것으로 알려진 변수만 참조하고(기밀 데이터는 포함하지 않음) 따옴표가 없는 다른 셸 특수 문자를 포함하지 않는 한 안전합니다. 문자열을 큰따옴표로 묶거나 여기 문서에서 확장해야 특별합니다 (앞에 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
나는 당신이 무슨 뜻인지 이해하지만, 이 답변 중 어느 것도 옳다고 생각하지 않습니다. 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
첫 번째 실행:너희 엄마 집에 가서 봤어디즈니 온 아이스.
당신이 그렇게한다면달필너는 성공 할거야.
그 다음에
_more_important_function():
나는 갔다묘지디즈니 온 아이스도 봤습니다.
당신이 그렇게한다면수학 과외너는 성공 할거야.
_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()
는( subshelled )
기능 - 포함합니다parens
대신에{ curly braces }
다른 사람들을 위해. 서브쉘에서 호출되기 때문에 설정하는 모든 변수는 다음과 같습니다.locally scoped
이 값은 상위 쉘로 돌아가면 사라집니다.
하지만 때_more_important_function()
세트$ACTION
이것은globally 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"}"
동일한 결과를 생성하지만 이 경우에도 중복됩니다. 저는 Shell을 선호합니다.: ${did:=nothing, gracefully}.
이를 통해 다음을 수행할 수 있습니다.
echo ${var:=something or other}
echo $var
something or other
something or other
여기에 파일
그런데 - null 또는 설정되지 않은 변수의 인라인 정의는 다음 방법이 작동하는 이유이기도 합니다.
<<HEREDOC echo $yo
${yo=yoyo}
HEREDOC
yoyo
그것에 대해 생각하는 가장 좋은 방법은here-document
실제 파일이 입력 파일 설명자로 스트리밍됩니다. 그것은 다소간 다르지만 다른 쉘은 약간 다르게 구현합니다.
어쨌든 인용하지 않으면<<LIMITER
스트리밍으로 받아요그리고다음과 같이 평가됨expansion.
따라서 변수를 선언하십시오.here-document
작동하지만 다음을 통해서만 가능합니다.expansion
이는 아직 설정되지 않은 변수를 설정하는 것으로 제한됩니다. 그러나 설명하신 대로 템플릿 인쇄 기능을 호출할 때 기본값이 항상 설정되므로 이는 귀하의 요구에 완벽하게 작동합니다.
왜 안 돼eval?
글쎄요, 제가 제시한 예는 안전하고 효율적인 수락 방법을 제공합니다.parameters.
범위를 다루기 때문에 집합의 각 변수는${parameter:=expansion}
외부적으로 정의할 수 있습니다. 따라서 이 모든 것을 template_pr.sh라는 스크립트에 넣고 다음을 실행하면:
% RESULT=something_else template_pr.sh
당신은 얻을 것이다:
너희 엄마 집에 가서 디즈니 온 아이스를 봤어
캘리그라피를 연습하면 뭔가가 생긴다
묘지에 가서 디즈니 온 아이스를 봤어
수학을 벼락치기하면 뭔가 얻을 수 있을 거야
묘지에 가서 디즈니 온 아이스를 봤어
수학을 벼락치기하면 뭔가 얻을 수 있을 거야
이는 스크립트에 문자 그대로 설정된 변수에는 작동하지 않습니다.$EVENT, $ACTION,
그리고$one,
하지만 저는 차이점을 보여주기 위해 이렇게 정의했습니다.
어쨌든 알 수 없는 입력을 수락합니다.evaled
명령문은 본질적으로 안전하지 않습니다.parameter expansion
이 목적을 위해 특별히 설계되었습니다.
답변3
확장되지 않은 변수 대신 문자열 템플릿에 대한 자리 표시자를 사용할 수 있습니다. 이것은 빨리 혼란스러울 수 있습니다. 수행하는 작업이 템플릿이 매우 많은 경우 실제 템플릿 라이브러리가 있는 언어를 고려할 수 있습니다.
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"
위의 단점은 템플릿 변수가 자체 단어여야 한다는 것입니다(예: you can't do "%prefix%foo"
). 이는 일부 수정을 통해 해결되거나 동적으로 대신 템플릿 변수를 하드코딩하여 해결할 수 있습니다.
답변4
나는 이것을 기반으로 할 것이다수락된 답변그리고 조금 단순화하세요. fill_template
전화 통화가 좀 어색한 것 같아요 . 환경 변수 대신 함수를 사용하려면 약간 다른 구문이 필요하지만(어떤 경우에는 작동하지 않을 수 있음) IMO에서는 이를 사용하는 코드를 제어하면 더 깔끔합니다.
"변수"를 함수로 정의합니다.
sentence1() {
echo "I went to ${PLACE} and saw ${EVENT}"
}
sentence2() {
echo "If you do ${ACTION} you will ${RESULT}"
}
그런 다음 다음과 같이 사용하십시오.
PLACE=Sydney; EVENT=fireworks
ACTION='not learn from history'; RESULT='have to relive history'
echo "During my holidays, $(sentence1)."
echo "Cicero said: \"$(sentence2)\"."