Bash 5.0.17(1)이 설치된 원격 CentOS에서 SSH를 통해 실행하는 유일한 사용자는 다음과 같습니다 read web_application_root
.
$HOME/www
또는 다음과 같이:
${HOME}/www
또는 다음과 같이:
"${HOME}"/www
또는 다음과 같이:
"${HOME}/www"
확장된 (환경) 변수를 사용하여 출력을 얻는 것을 목표 MY_USER_HOME_DIRECTORY/www
로 합니다.
ls -la $HOME/www
제대로 작동하는 동안 ls -la $web_application_root
모든 예제가 실패합니다. 오류 예제는 다음과 같습니다.
ls: cannot access '$HOME/www': No such file or directory
내가 아는 한, read
위의 모든 $HOME
변형은 (오류의 작은따옴표로 인해) 문자열로 처리되므로 확장하지 않습니다.
내부 변수를 확장하고 읽는 방법은 무엇입니까?
답변1
변수는 에 전달될 때 확장되지 않습니다 read
. $VAR
s 또는 ${VAR}
s가 VAR
기존 환경 변수의 이름을 나타내는 경우 (이름이 ASCII 문자 또는 밑줄로 시작하고 그 뒤에 ASCII 숫자 또는 밑줄이 오는 변수만) 다른 모든 단어 확장( $non_exported_shell_variable
, $1
, $#
, ${HOME+x}
, $((1 + 1))
, $(cmd)
...) 변경하지 않으려면 envsubst
(GNU에서 gettext
) 다음을 사용할 수 있습니다.
IFS= read -r web_application_root || exit
web_application_root=$(printf %s "$web_application_root" | envsubst)
ls -la -- "$web_application_root"
변수 이름을 인수로 사용하고 읽기 및 환경 변수 확장을 수행하는 쉘 함수로 만들 수 있습니다.
read_one_line_and_expand_envvars() {
IFS= read -r "$1"
ret="$?"
command eval "$1"'=$(printf %s "${'"$1"'}" | envsubst)' && return "$ret"
}
예를 들어 다음과 같이 사용됩니다.
printf >&2 'Please enter the root dir (${ENVVAR} expanded): '
read_one_line_and_expand_envvars web_application_root || exit
printf >&2 'The expanded version of your input is "%s"\n' "$web_application_root"
제한된 환경 변수 세트로 대체를 제한하려면 $VAR1$VAR2...
목록을 리터럴 인수로 다음에 전달할 수 있습니다 envsubst
.
web_application_root=$(
printf %s "$web_application_root" |
envsubst '$HOME$MYENVVAR'
)
( 여기서는 envsubst
입력에서 $HOME
, ${HOME}
및 만 바꾸고 다른 모든 s는 변경하지 않도록 지시합니다 .)$MYENVVAR
${MYENVVAR}
$VAR
모든 형태의 단어 확장을 허용하려면(그러나 이것이 명령 주입 취약점이 된다는 점에 유의하십시오) 다음을 수행할 수 있습니다.
web_application_root=$(eval "cat << __EOF__
$web_application_root
__EOF__")
또는 변수 이름을 인수로 사용하는 함수로:
read_one_line_and_perform_shell_word_expansions() {
IFS= read -r "$1"
ret=$?
command eval '
case "${'"$1"'}" in
(EOF) ;;
(*)
'"$1"'=$(command eval "cat << EOF
${'"$1"'}
EOF")
esac' && return "$ret"
}
printf >&2 'Please enter the root dir ($var/$((...))/$(cmd) allowed): '
read_one_line_and_perform_shell_word_expansions web_application_root || exit
printf >&2 'The expanded version of your input is "%s"\n' "$web_application_root"
자세한 인라인 문서와 동일한 기능:
read_one_line_and_perform_shell_word_expansions() {
# first argument of our function is the variable name or REPLY
# if not specified.
varname=${1-REPLY}
# read one line from stdin with read's unwanted default post-processing
# (which is otherwise dependant on the current value of $IFS) disabled.
IFS= read -r "$varname"
# record read's exit status. If it's non zero, a full line could not be
# read. We may still want to perform the expansions in whatever much
# was read, and pass that exit status to the caller so they decide what
# to do with it.
ret=$?
# We prefix the "eval" special builtin with "command" to make it lose
# its "special" status (namely here, exit the script about failure,
# something bash only does when in standard mode).
command eval '
# the approach we take to expand word expansions would be defeated
# if the user entered "EOF" which is the delimiter we chose for our
# here-document, so we need to handle it as a special case:
case "${'"$varname"'}" in
(EOF) ;;
(*)
# the idea here is to have the shell evaluate the
# myvar=$(command eval "cat << EOF
# ${myvar}
# EOF")
#
# shell code when $1 is myvar, so that the
#
# cat << EOF
# contents of $myvar with $(cmd), $ENV and all
# EOF
#
# shell code be evaluated, and those $(cmd), $ENV expansions
# performed in the process
'"$varname"'=$(command eval "cat << EOF
${'"$varname"'}
EOF")
esac' &&
# unless eval itself failed, return read's exit status to the caller:
return "$ret"
}
하지만 문제는 XY 문제처럼 들립니다. 그렇게 해서 입력을 얻는 것은 read
번거롭고 비실용적입니다. 인수를 통해 입력을 얻는 것이 더 낫습니다. 그런 다음 확장을 위해 호출자의 셸에 맡길 수 있습니다.그들을그렇게 할 생각입니다.
바꾸다
#! /bin/sh -
IFS= read -r var
ls -l -- "$var"
(그리고 기억해전화하는 read
것과 IFS=
전화하지 않는 -r
것은 거의 당신이 원하는 것이 아니다).
완료:
#! /bin/sh -
var=${1?}
ls -l -- "$var"
그런 다음 호출자는 적합하다고 판단되는 작업 your-script ~/dir
을 수행 할 수 있습니다.your-script "$HOME/dir"
your-script '$$$weird***/dir'
your-script $'/dir\nwith\nnewline\ncharacters'
^단어 확장이러한 맥락에서는 다음을 가리킨다.매개변수 확장,산술 확장그리고명령 대체. 여기에는 포함되지 않습니다.파일 이름 생성(일명와일드카드또는경로명 확장),물결표 확장...도 아니다버팀대 확장(표준 sh
기능 자체는 아닙니다). 여기에서 문서를 사용하면 '
및 "
가 변경되지 않은 상태로 유지되지만 여전히 백슬래시 처리가 있다는 점에 유의하세요.