
bash
Bash에서 사용자 입력을 읽으면서 변수 확장을 허용하는 방법이 있습니까 ?
프로그램 도중에 사용자에게 경로 입력을 요청하려고 하는데 ~
및 기타 변수가 내장의 일부로 확장되지 않기 때문에 read
사용자는 절대 경로를 입력해야 합니다.
예: 사용자가 다음에 대한 경로를 입력하는 경우:
read -ep "input> " dirin
[[ -d "$dirin" ]]
사용자가 입력 /home/user/bin
했지만 ~/bin
또는 입력하지 않은 경우 true를 반환합니다 $HOME/bin
.
답변1
순진한 방법은 다음과 같습니다
eval "dirin=$dirin"
그것이 하는 일은 dirin=$dirin
쉘 코드로서의 확장을 평가하는 것입니다.
dirin
포함을 사용하면 ~/foo
실제로 다음을 평가합니다.
dirin=~/foo
한계를 쉽게 알 수 있습니다. dirin
포함 을 사용하면 foo bar
다음과 같습니다.
dirin=foo bar
bar
따라서 해당 환경 에서 실행되고 있습니다 dirin=foo
(그리고 모든 쉘 특수 문자에 다른 문제가 있을 수 있습니다).
여기서는 허용되는 확장(물결표, 명령 대체, 매개변수 확장, 프로세스 대체, 산술 확장, 파일 이름 확장...)을 결정하고 이러한 대체를 직접 수행하거나 다음을 사용하여 수행해야 합니다.eval
.탈출하다~foo
예를 들어 , $VAR
, 로 제한하지 않는 한 전체 쉘 구문 분석기를 구현하는 것 외에는 사실상 불가능할 문자를 제외한 모든 문자 ${VAR}
.
여기서는 그 zsh
대신 bash
에 전용 연산자를 사용하겠습니다.
vared -cp "input> " dirin
printf "%s\n" "${(e)dirin}"
vared
은변수 편집기, 와 유사 bash
합니다 read -e
.
(e)
매개변수의 내용에서 확장(매개변수, 명령, 산술(물결표 제외))을 수행하는 매개변수 확장 플래그입니다.
문자열의 시작 부분에서만 발생하는 물결표 확장을 해결하려면 다음을 수행합니다.
vared -cp "input> " dirin
if [[ $dirin =~ '^(~[[:alnum:]_.-]*(/|$))(.*)' ]]; then
eval "dirin=$match[1]\${(e)match[3]}"
else
dirin=${(e)dirin}
fi
POSIXly에서는 bash
물결표와 변수(매개변수 아님) 확장을 수행하려면 다음과 같은 함수를 작성할 수 있습니다.
expand_var() {
eval "_ev_var=\${$1}"
_ev_outvar=
_ev_v=${_ev_var%%/*}
case $_ev_v in
(?*[![:alnum:]._-]*) ;;
("~"*)
eval "_ev_outvar=$_ev_v"; _ev_var=${_ev_var#"$_ev_v"}
esac
while :; do
case $_ev_var in
(*'$'*)
_ev_outvar=$_ev_outvar${_ev_var%%"$"*}
_ev_var=${_ev_var#*"$"}
case $_ev_var in
('{'*'}'*)
_ev_v=${_ev_var%%\}*}
_ev_v=${_ev_v#"{"}
case $_ev_v in
"" | [![:alpha:]_]* | *[![:alnum:]_]*) _ev_outvar=$_ev_outvar\$ ;;
(*) eval "_ev_outvar=\$_ev_outvar\${$_ev_v}"; _ev_var=${_ev_var#*\}};;
esac;;
([[:alpha:]_]*)
_ev_v=${_ev_var%%[![:alnum:]_]*}
eval "_ev_outvar=\$_ev_outvar\$$_ev_v"
_ev_var=${_ev_var#"$_ev_v"};;
(*)
_ev_outvar=$_ev_outvar\$
esac;;
(*)
_ev_outvar=$_ev_outvar$_ev_var
break
esac
done
eval "$1=\$_ev_outvar"
}
예:
$ var='~mail/$USER'
$ expand_var var;
$ printf '%s\n' "$var"
/var/mail/stephane
대략적으로 다음 ~${}-_.
을 전달하기 전에 모든 문자 앞에 백슬래시를 붙일 수도 있습니다 eval
.
eval "dirin=$(
printf '%s\n' "$dirin" |
sed 's/[^[:alnum:]~${}_.-]/\\&/g')"
(여기서는 $dirin
개행 문자를 포함할 수 없으므로 단순화되었습니다 read
.)
예를 들어 입력하면 구문 오류가 발생 ${foo#bar}
하지만 최소한 단순한 eval
경우만큼 해를 끼칠 수는 없습니다.
편집하다: 기타 POSIX 쉘에 대한 작업 솔루션은 bash
물결표와 in zsh
및 use 와 같은 기타 확장을 분리하는 것입니다.eval
여기 문서와 함께다른 확장다음과 같은 부분:
expand_var() {
eval "_ev_var=\${$1}"
_ev_outvar=
_ev_v=${_ev_var%%/*}
case $_ev_v in
(?*[![:alnum:]._-]*) ;;
("~"*)
eval "_ev_outvar=$_ev_v"; _ev_var=${_ev_var#"$_ev_v"}
esac
eval "$1=\$_ev_outvar\$(cat << //unlikely//
$_ev_var
//unlikely//
)"
그러면 위와 같이 물결표, 매개변수, 산술 및 명령 확장이 가능해집니다 zsh
. }