Um jeito ingênuo seria:
eval "dirin=$dirin"
O que isso faz é avaliar a expansão de dirin=$dirin
como código shell.
Com dirin
contendo ~/foo
, está realmente avaliando:
dirin=~/foo
É fácil ver as limitações. Com um dirin
contendo foo bar
, isso se torna:
dirin=foo bar
Portanto, ele está executando bar
com dirin=foo
em seu ambiente (e você teria outros problemas com todos os caracteres especiais do shell).
Aqui, você precisaria decidir quais expansões são permitidas (til, substituição de comando, expansão de parâmetro, substituição de processo, expansão aritmética, expansão de nome de arquivo ...) e fazer essas substituições manualmente ou usar eval
, mas escape todos os caracteres, exceto aqueles que permitiriam que seriam virtualmente impossíveis a não ser implementando um analisador de sintaxe de shell completo, a menos que você o limite, por exemplo ~foo
, $VAR
, ${VAR}
. / p>
Aqui, eu usaria zsh
em vez de bash
que tem um operador dedicado para isso:
vared -cp "input> " dirin
printf "%s\n" "${(e)dirin}"
vared
é o editor de variáveis , semelhante a bash
' read -e
.
(e)
é um sinalizador de expansão de parâmetro que executa expansões (parâmetro, comando, aritmético mas não til) no conteúdo do parâmetro.
Para resolver a expansão de til, que ocorre apenas no início da string, faríamos:
vared -cp "input> " dirin
if [[ $dirin =~ '^(~[[:alnum:]_.-]*(/|$))(.*)' ]]; then
eval "dirin=$match[1]\${(e)match[3]}"
else
dirin=${(e)dirin}
fi
POSIXly (assim bash
ly também), para executar a expansão de til e variável (não de parâmetro), você poderia escrever uma função como:
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"
}
Exemplo:
$ var='~mail/$USER'
$ expand_var var;
$ printf '%s\n' "$var"
/var/mail/stephane
Como uma aproximação, podemos também preceder todos os caracteres, mas ~${}-_.
e alnums com barra invertida antes de passar para eval
:
eval "dirin=$(
printf '%s\n' "$dirin" |
sed 's/[^[:alnum:]~${}_.-]/\&/g')"
(aqui simplificado com base no fato de que $dirin
não pode conter caracteres de nova linha, pois é proveniente de read
)
Isso acionaria erros de sintaxe se alguém inserisse ${foo#bar}
, por exemplo, mas pelo menos isso não prejudicasse muito como um simples eval
faria.
Editar : uma solução de trabalho para bash
e outros POSIX shells seria separar o til e outras expansões como em zsh
e usar eval
com um documento aqui para o < em> outras expansões parte como:
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//
)"
Isso permitiria expansões de til, parâmetro, aritmética e comando, como em zsh
acima.
}