In bash, is there any way to read in user input but still allow bash variable expansion?I am trying to request the user enter a path in the middle of a program but since ~ and other variables are not expanded as a part of the read builtin, users have to enter in an absolute path.Example: When a user enters a path into:read -ep input>  dirin  [[ -d $dirin ]] returns true when a user enters /home/user/bin but not ~/bin or $HOME/bin.
Can Bash Variable Expansion be performed directly on user input?
shell script;wildcards;variable substitution;read
A naive way would be:eval dirin=$dirinWhat that does is evaluate the expansion of dirin=$dirin as shell code.With dirin containing ~/foo, it's actually evaluating:dirin=~/fooIt's easy to see the limitations. With a dirin containing foo bar, that becomes:dirin=foo barSo it's running bar with dirin=foo in its environment (and you'd have other problems with all the shell special characters).Here, you'd need to decide what expansions are allowed (tilde, command substitution, parameter expansion, process substitution, arithmetic expansion, filename expansion...) and either do those substitutions by hand, or use eval but escape every character but those that would allow them which would be virtually impossible other than by implementing  a full shell syntax parser unless you limit it to for instance ~foo, $VAR, ${VAR}.Here, I'd use zsh instead of bash that has a dedicated operator for that:vared -cp input>  dirinprintf %s\n ${(e)dirin}vared is the variable editor, similar to bash's read -e.(e) is a parameter expansion flag that performs expansions (parameter, command, arithmetic but not tilde) in the content of the parameter.To address tilde expansion, which only takes place at the beginning of the string, we'd do:vared -cp input>  dirinif [[ $dirin =~ '^(~[[:alnum:]_.-]*(/|$))(.*)' ]]; then  eval dirin=$match[1]\${(e)match[3]}else  dirin=${(e)dirin}fiPOSIXly (so bashly as well), to perform tilde and variable (not parameter) expansion, you could write a function like: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}Example:$ var='~mail/$USER'$ expand_var var;$ printf '%s\n' $var/var/mail/stephaneAs an approximation, we could also prepend every character but ~${}-_. and alnums with backslash before passing to eval:eval dirin=$(  printf '%s\n' $dirin |    sed 's/[^[:alnum:]~${}_.-]/\\&/g')(here simplified on the ground that $dirin can't contain newline characters as it comes from read)That would trigger syntax errors if one entered ${foo#bar} for instance but at least that can't do much harm as a simple eval would.Edit: a working solution for bash and other POSIX shells would be to separate the tilde and other expansions like in zsh and use eval with a here-document for the other expansions part like: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//)That would allow tilde, parameter, arithmetic and command expansions like in zsh above.    }