
在bash中,有沒有辦法讀取用戶輸入但仍然允許bash
變數擴展?
我試圖請求使用者在程式中間輸入路徑,但由於~
其他變數沒有作為內建的一部分擴展read
,因此使用者必須輸入絕對路徑。
範例:當使用者輸入路徑時:
read -ep "input> " dirin
[[ -d "$dirin" ]]
當使用者輸入/home/user/bin
但未輸入~/bin
或 時傳回 true $HOME/bin
。
答案1
一個天真的方法是:
eval "dirin=$dirin"
dirin=$dirin
它的作用是評估as shell 程式碼的擴展。
對於dirin
contains ~/foo
,它實際上是在評估:
dirin=~/foo
很容易看出其限制。如果dirin
包含foo bar
,則變為:
dirin=foo bar
bar
所以它在它的環境中運行dirin=foo
(並且所有 shell 特殊字元都會遇到其他問題)。
在這裡,您需要決定允許哪些擴展(波形符、命令替換、參數擴展、進程替換、算術擴展、文件名擴展...),並且要么手動執行這些替換,要么使用eval
but逃脫除了那些允許它們的字符之外的每個字符,除了實現完整的 shell 語法解析器之外,這實際上是不可能的,除非您將其限制為例如~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
作為近似,我們也可以~${}-_.
在傳遞給之前在每個字元 but 和 alnums 前面加上反斜線eval
:
eval "dirin=$(
printf '%s\n' "$dirin" |
sed 's/[^[:alnum:]~${}_.-]/\\&/g')"
(這裡進行了簡化,$dirin
不能包含換行符,因為它來自read
)
${foo#bar}
例如,如果有人輸入,這會觸發語法錯誤,但至少不會像簡單的那樣造成太大傷害eval
。
編輯: 和其他 POSIX shell 的一個可行解決方案bash
是將波形符號和其他擴充分開,例如 in並與此處文件一起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
。 }