Perl을 사용 하는 것이 옵션이고 모든 쉘 변수 와 달리 환경 변수만을 기반으로 확장하는 내용이라면 Stuart P. Bentley의 강력한 답변을 고려하십시오 . .
이 답변의 목표는 제공하는 bash는 전용 솔루션 의 사용에도 불구하고 - 그렇게 eval
해야 - 사용하는 것이 안전합니다 .
목표는 다음과 같습니다
- 가변 참조
${name}
및 $name
가변 참조의 확장을 지원합니다 .
- 다른 모든 확장을 방지하십시오.
- 명령 대체 (
$(...)
및 레거시 구문 `...`
)
- 산술 대체 (
$((...))
및 레거시 구문 $[...]
).
- 접두사
\
( \${name}
) 로 변수 확장을 선택적으로 억제 할 수 있습니다.
- 특수 문자를 유지하십시오. 입력에서 현저
"
하고 \
인스턴스.
- 인수 또는 stdin을 통한 입력을 허용하십시오.
기능expandVars()
:
expandVars() {
local txtToEval=$* txtToEvalEscaped
# If no arguments were passed, process stdin input.
(( $# == 0 )) && IFS= read -r -d '' txtToEval
# Disable command substitutions and arithmetic expansions to prevent execution
# of arbitrary commands.
# Note that selectively allowing $((...)) or $[...] to enable arithmetic
# expressions is NOT safe, because command substitutions could be embedded in them.
# If you fully trust or control the input, you can remove the `tr` calls below
IFS= read -r -d '' txtToEvalEscaped < <(printf %s "$txtToEval" | tr '`([' '\1\2\3')
# Pass the string to `eval`, escaping embedded double quotes first.
# `printf %s` ensures that the string is printed without interpretation
# (after processing by by bash).
# The `tr` command reconverts the previously escaped chars. back to their
# literal original.
eval printf %s "\"${txtToEvalEscaped//\"/\\\"}\"" | tr '\1\2\3' '`(['
}
예 :
$ expandVars '\$HOME="$HOME"; `date` and $(ls)'
$HOME="/home/jdoe"; `date` and $(ls) # only $HOME was expanded
$ printf '\$SHELL=${SHELL}, but "$(( 1 \ 2 ))" will not expand' | expandVars
$SHELL=/bin/bash, but "$(( 1 \ 2 ))" will not expand # only ${SHELL} was expanded
- 성능상의 이유로이 함수는 stdin 입력 을 한 번 에 메모리로 읽지 만 함수를 한 줄씩 접근하는 것은 쉽습니다.
- 또한 내장 명령이나 산술 대체가 포함되어 있지 않은 경우 와 같은 비 기본 변수 확장 도 지원합니다.
${HOME:0:10}
${HOME:0:$(echo 10)}
- 이러한 내장 대체는 실제로 기능을 중단합니다 (모두
$(
및 `
인스턴스가 맹목적으로 이스케이프 되기 때문에 ).
- 마찬가지로
${HOME
(닫기 누락 }
) BREAK 함수 와 같은 잘못된 변수 참조
- bash가 큰 따옴표로 묶은 문자열을 처리하기 때문에 백 슬래시는 다음과 같이 처리됩니다.
\$name
확장을 방지합니다.
\
뒤에 하나도 없는$
는 그대로 유지됩니다.
- 인접한 여러
\
인스턴스 를 나타내 려면 두 배로 늘려야합니다 . 예 :
- 내부 목적으로 사용되는 다음을 포함 할 수 없습니다 입력 (거의 사용하지 않음) 문자 :
0x1
, 0x2
, 0x3
.
- bash가 새로운 확장 구문을 도입해야한다면이 함수가 그러한 확장을 막지 못할 수도 있다는 가설이 있습니다
eval
. 사용하지 않는 솔루션에 대해서는 아래를 참조하십시오 .
확장 만 지원 하는 보다 제한적인 솔루션을${name}
찾고있는 경우 ( 예 : 필수 중괄호 사용) 무시$name
참조 이 답변 을 참조하십시오 .
여기는 허용 된 답변 의 bash 전용 eval
무료 솔루션 향상된 버전은 .
개선 사항은 다음과 같습니다.
${name}
및 모두 확장 지원$name
가변 참조 가변 참조의 .
- 을지 지하다
\
확장해서는 안되는 -escaping 변수 참조를 .
- 달리
eval
위 기반 솔루션
- 비 기본 확장은 무시됩니다
- 잘못된 변수 참조는 무시됩니다 (스크립트를 손상시키지 않습니다)
IFS= read -d '' -r lines # read all input from stdin at once
end_offset=${#lines}
while [[ "${lines:0:end_offset}" =~ (.*)\$(\{([a-zA-Z_][a-zA-Z_0-9]*)\}|([a-zA-Z_][a-zA-Z_0-9]*))(.*) ]] ; do
pre=${BASH_REMATCH[1]} # everything before the var. reference
post=${BASH_REMATCH[5]}${lines:end_offset} # everything after
# extract the var. name; it's in the 3rd capture group, if the name is enclosed in {...}, and the 4th otherwise
[[ -n ${BASH_REMATCH[3]} ]] && varName=${BASH_REMATCH[3]} || varName=${BASH_REMATCH[4]}
# Is the var ref. escaped, i.e., prefixed with an odd number of backslashes?
if [[ $pre =~ \\+$ ]] && (( ${#BASH_REMATCH} % 2 )); then
: # no change to $lines, leave escaped var. ref. untouched
else # replace the variable reference with the variable's value using indirect expansion
lines=${pre}${!varName}${post}
fi
end_offset=${#pre}
done
printf %s "$lines"