内部変数を拡張して読み取るには?

内部変数を拡張して読み取るには?

Bash 5.0.17(1)がインストールされているリモートCentOSでSSH経由で実行している唯一のユーザーは次のとおりですread web_application_root

$HOME/www

または次のように:

${HOME}/www

または次のように:

"${HOME}"/www

または次のように:

"${HOME}/www"

拡張(環境)変数を使用して出力を取得することを目的としていMY_USER_HOME_DIRECTORY/wwwます。

ls -la $HOME/www正常に動作している間、ls -la $web_application_rootすべての例は失敗します。エラーの例は次のとおりです。

ls: cannot access '$HOME/www': No such file or directory

私が知っている限り、read上記のすべての$HOMEバリアントは(エラーの一重引用符のため)文字列として扱われるため、拡張しません。

内部変数を拡張して読み取るには?

ベストアンサー1

変数はに渡されると拡張されませんread$VARsまたは${VAR}sがVAR既存の環境変数の名前を表す場合(名前がASCII文字またはアンダースコアで始まり、その後にASCII数字またはアンダースコアが続く変数のみ)、他のすべての単語拡張(、、、、、、、... $non_exported_shell_variable)は変更されません。(GNUでは)以下を使用できます。$1$#${HOME+x}$((1 + 1))$(cmd)envsubstgettext

IFS= read -r web_application_root || exit
web_application_root=$(printf %s "$web_application_root" | envsubst)
ls -la -- "$web_application_root"

変数名を引数として使用し、読み取りおよび環境変数拡張を実行するシェル関数にすることができます。

read_one_line_and_expand_envvars() {
  IFS= read -r "$1"
  ret="$?"
  command eval "$1"'=$(printf %s "${'"$1"'}" | envsubst)' && return "$ret"
}

たとえば、次のように使用されます。

printf >&2 'Please enter the root dir (${ENVVAR} expanded): '
read_one_line_and_expand_envvars web_application_root || exit
printf >&2 'The expanded version of your input is "%s"\n' "$web_application_root"

限られた一連の環境変数に置換を制限するには、$VAR1$VAR2...リストをリテラル引数として次に渡すことができますenvsubst

web_application_root=$(
  printf %s "$web_application_root" |
    envsubst '$HOME$MYENVVAR'
)

(ここではenvsubst入力で$HOME${HOME}およびのみを変更し、他のすべてのsは変更しないように指示します。)$MYENVVAR${MYENVVAR}$VAR

すべての形式の単語拡張を許可するには(ただし、これがコマンドインジェクションの脆弱性になることに注意してください)、次のことを実行できます。

web_application_root=$(eval "cat << __EOF__
$web_application_root
__EOF__")

または変数名を引数として使用する関数として:

read_one_line_and_perform_shell_word_expansions() {
  IFS= read -r "$1"
  ret=$?
  command eval '
    case "${'"$1"'}" in
      (EOF) ;;
      (*)
        '"$1"'=$(command eval "cat << EOF
${'"$1"'}
EOF")
    esac' && return "$ret"
}
printf >&2 'Please enter the root dir ($var/$((...))/$(cmd) allowed): '
read_one_line_and_perform_shell_word_expansions web_application_root || exit
printf >&2 'The expanded version of your input is "%s"\n' "$web_application_root"

詳細なインライン文書と同じ機能:

read_one_line_and_perform_shell_word_expansions() {
  # first argument of our function is the variable name or REPLY
  # if not specified.
  varname=${1-REPLY}

  # read one line from stdin with read's unwanted default post-processing
  # (which is otherwise dependant on the current value of $IFS) disabled.
  IFS= read -r "$varname"

  # record read's exit status. If it's non zero, a full line could not be
  # read. We may still want to perform the expansions in whatever much
  # was read, and pass that exit status to the caller so they decide what
  # to do with it.
  ret=$?

  # We prefix the "eval" special builtin with "command" to make it lose
  # its "special" status (namely here, exit the script about failure,
  # something bash only does when in standard mode).
  command eval '
    # the approach we take to expand word expansions would be defeated
    # if the user entered "EOF" which is the delimiter we chose for our
    # here-document, so we need to handle it as a special case:
    case "${'"$varname"'}" in
      (EOF) ;;
      (*)
        # the idea here is to have the shell evaluate the
        # myvar=$(command eval "cat << EOF
        # ${myvar}
        # EOF")
        #
        # shell code when $1 is myvar, so that the
        #
        # cat << EOF
        # contents of $myvar with $(cmd), $ENV and all
        # EOF
        #
        # shell code be evaluated, and those $(cmd), $ENV expansions
        # performed in the process
        '"$varname"'=$(command eval "cat << EOF
${'"$varname"'}
EOF")
    esac' &&
      # unless eval itself failed, return read's exit status to the caller:
      return "$ret"
}

しかし、問題はXYの問題のように聞こえます。そうして入力を得るのは面倒でread非実用的です。引数を介して入力を取得する方が良いです。その後、拡張のために呼び出し側のシェルに任せることができます。それらそうするつもりです。

変える

#! /bin/sh -
IFS= read -r var
ls -l -- "$var"

(そして覚えている電話するreadこととIFS=電話しない-rことはほとんどあなたが望むものではありません)。

完了:

#! /bin/sh -
var=${1?}
ls -l -- "$var"

その後、呼び出し側は適切であると判断された操作をyour-script ~/dir実行できます。your-script "$HOME/dir"your-script '$$$weird***/dir'your-script $'/dir\nwith\nnewline\ncharacters'


^単語拡張この文脈では、以下を指します。パラメータ拡張算術拡張そしてコマンドの置き換え。これには含まれません。ファイル名の生成(別名ワイルドカードまたはパス名拡張)、チルダ拡張...でもない支柱の拡張(標準sh機能自体ではありません)。ここでドキュメントを使用すると、およびは変更されていません'"、まだバックスラッシュ処理があることに注意してください。

おすすめ記事