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
。 }