Bash에서 사용자 입력을 읽으면서도 bash
변수 확장을 허용하는 방법이 있습니까?
프로그램 중간에 사용자에게 경로를 입력하라고 하려고 하는데, ~
내장된 부분으로 다른 변수들이 확장되지 않기 때문에 read
사용자가 절대 경로를 입력해야 합니다.
예: 사용자가 경로를 입력하는 경우:
read -ep "input> " dirin
[[ -d "$dirin" ]]
사용자가 입력했지만 /home/user/bin
입력하지 않은 경우 ~/bin
또는 을 반환합니다 $HOME/bin
.
답변1
순진한 접근 방식은 다음과 같습니다.
eval "dirin=$dirin"
dirin=$dirin
그 목적은 쉘 코드의 확장으로 평가하는 것입니다.
dirin
Contains 의 경우 ~/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
또한 true), 물결표 및 변수(매개변수 대신) 확장을 수행하려면 다음과 같은 함수를 작성할 수 있습니다.
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
물결표와 에서와 같은 기타 확장을 분리하고 여기 문서와 함께 zsh
사용하는 것입니다.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
. }