関数内のランダムなアンバウンド変数エラー

関数内のランダムなアンバウンド変数エラー

Bashで関数を作成し、呼び出すと崩れる間違ったunbound variable。変数だからわかりません。束縛されていない発表されました。また、時には66ラインで衝突が発生し、時には76ラインで衝突が発生し、時には86ラインで衝突が発生するかのようにランダムにトリガーされるようです。

機能は次のとおりです。

#!/usr/bin/env bash

function setConfigLS() {
    declare DFLT_CFG_FILE="${WEB_DOCUMENT_ROOT}/application/config/config.php"
    declare DFLT_ARRAY='config'
    declare cfgFile="$DFLT_CFG_FILE"
    declare array="$DFLT_ARRAY"
    declare value key arg
    declare -a args=()

    while (( $# > 0 )); do
        arg="$1" && shift
        case "$arg" in
            --file=*)
                cfgFile="${arg#*=}"
            ;;
            -f|--file)
                cfgFile="$1"
                shift
            ;;
            --value=*)
                value="${arg#*=}"
            ;;
            -v|--value)
                value="$1"
                shift
            ;;
            --key=*)
                key="${arg#*=}"
            ;;
            -k|--key)
                key="$1"
                shift
            ;;
            --array=*)
                array="${arg#*=}"
            ;;
            -a|--array)
                array="$1"
                shift
            ;;
            -h|--help)
                echo >&2 'Set a LimeSurvey configuration option.'
                echo >&2 ''
                echo >&2 'Usage:'
                echo >&2 '  setConfigLS [options...] <KEY> <VALUE>'
                echo >&2 '  setConfigLS [options...] --value=<VALUE> --key=<KEY>'
                echo >&2 ''
                echo >&2 'Options:'
                echo >&2 '  --file, -f <CONFIG_FILE>  LimeSurvey configuration file.'
                echo >&2 "                              Default: ${DFLT_CFG_FILE}"
                echo >&2 '  --array, -a <ARRAY>       Name of array containing the configuration.'
                echo >&2 "                              Default: ${DFLT_ARRAY}"
                echo >&2 '  --key, --k <KEY>          Key of the configuration option to set. (required)'
                echo >&2 '  --value, -v <VALUE>       Value of the configuration option. (required)'
                echo >&2 '  --help, -h                Prints this message.'
                echo >&2 ''
                return 0
            ;;
            *)
                args+=( "$arg" )
            ;;
        esac
    done

    if [ -z "$key" ]; then # line 66: key: unbound variable
        if (( ${#args} > 0 )); then
            key="${args[0]}"
            args=( "${args[@]:1}" )
        else
            echo 'Error: `--key` is required' >&2
            return 1
        fi
    fi

    if [ -z "$value" ]; then # line 76: value: unbound variable
        if (( ${#args} > 0 )); then
            value="${args[0]}"
            args=( "${args[@]:1}" )
        else
            echo 'Error: `--value` is required' >&2
            return 1
        fi
    fi

    if (( ${#args} > 0 )); then # line 86: args: unbound variable
        echo 'Error: too many arguments' >&2
        return 1
    fi

    array="${array//\//\\\/}"
    value="${value//$'\n'/\\$'\n'}"

    ssed -Ri "$cfgFile" \
        -e 's~^(\s*)('"${array}"'\s*=>\s*array\s*\()((?:\([^)]*\)|[^)])+)~\1\2\n\1    \3\n\1~'

    ssed -Ri "$cfgFile" \
        -e '/^\s*'"${array}"'\s*=>\s*array\s*\([^)]*$/ {
                :a
                n
                s~^((?:\s*(?:[^,/\s]|/[^/]))+)(\s*//.*)?$~\1,\2~
                s~^(\s*)//\s*('"${key//~/\\~}"'\s*=>)~\1\2~
                /^\s*\)/ {
                    i \        '"${key}"'=>'"${value}"',
                    bq
                }
                /^\s*'"${key//\//\\\/}"'\s*=>/ {
                    s~>.*~>'"${value//~/\\~}"',~
                    bq
                }
                ba
                :q
            }'
}

に変更しようとしましたdeclare value key arg...

declare value=
declare key=
declare arg=

…しかし、それは何も変わりません。

少し混乱しています!私が逃したものは何ですか?見えないものはありますか?


編集1

この関数はubuntu 18.04ベースのdockerイメージのエントリポイントスクリプトから呼び出されます。実際、私はこの写真

関数のファイルがにコピーされます/opt/docker/functions/set-config-ls.sh

以下は、関数を呼び出すスクリプトです。

#!/usr/bin/env bash
set -eu

declare FUNC_DIR='/opt/docker/functions'
declare APP_DIR="${WEB_DOCUMENT_ROOT}"
declare DB_SETUP_PHP="/opt/docker/db_setup.php"

source "${FUNC_DIR}/tty-loggers.sh"
source "${FUNC_DIR}/yes-no.sh"
source "${FUNC_DIR}/file-env.sh"
source "${FUNC_DIR}/set-config-ls.sh"
source "${FUNC_DIR}/env-list-vars.sh"


####################################################################
########################## Setup Variables #########################

fileEnv 'LIMESURVEY_DB_TYPE' 'mysql'
fileEnv 'LIMESURVEY_DB_HOST' 'mysql'
fileEnv 'LIMESURVEY_DB_PORT' '3306'
fileEnv 'LIMESURVEY_TABLE_PREFIX' ''
fileEnv 'LIMESURVEY_ADMIN_NAME' 'Lime Administrator'
fileEnv 'LIMESURVEY_ADMIN_EMAIL' '[email protected]'
fileEnv 'LIMESURVEY_ADMIN_USER' ''
fileEnv 'LIMESURVEY_ADMIN_PASSWORD' ''
fileEnv 'LIMESURVEY_DEBUG' '0'
fileEnv 'LIMESURVEY_SQL_DEBUG' '0'
fileEnv 'MYSQL_SSL_CA' ''
fileEnv 'LIMESURVEY_USE_INNODB' ''

# if we're linked to MySQL and thus have credentials already, let's use them
fileEnv 'LIMESURVEY_DB_NAME' "${MYSQL_ENV_MYSQL_DATABASE:-limesurvey}"
fileEnv 'LIMESURVEY_DB_USER' "${MYSQL_ENV_MYSQL_USER:-root}"

if [ "${LIMESURVEY_DB_USER}" = 'root' ]; then
    fileEnv 'LIMESURVEY_DB_PASSWORD' "${MYSQL_ENV_MYSQL_ROOT_PASSWORD:-}"
else
    fileEnv 'LIMESURVEY_DB_PASSWORD' "${MYSQL_ENV_MYSQL_PASSWORD:-}"
fi

if [ -z "${LIMESURVEY_DB_PASSWORD}" ]; then
    logError 'error: missing required LIMESURVEY_DB_PASSWORD environment variable' >&2
    logError '  Did you forget to -e LIMESURVEY_DB_PASSWORD=... ?' >&2
    logError '' >&2
    logError '  (Also of interest might be LIMESURVEY_DB_USER and LIMESURVEY_DB_NAME.)' >&2
    exit 1
fi

declare -A CONNECTION_STRINGS=(
    [mysql]="mysql:host=${LIMESURVEY_DB_HOST};port=${LIMESURVEY_DB_PORT};dbname=${LIMESURVEY_DB_NAME};"
    [dblib]="dblib:host=${LIMESURVEY_DB_HOST};dbname=${LIMESURVEY_DB_NAME}"
    [pgsql]="pgsql:host=${LIMESURVEY_DB_HOST};port=${LIMESURVEY_DB_PORT};user=${LIMESURVEY_DB_USER};password=${LIMESURVEY_DB_PASSWORD};dbname=${LIMESURVEY_DB_NAME};"
    [sqlsrv]="sqlsrv:Server=${LIMESURVEY_DB_HOST};Database=${LIMESURVEY_DB_NAME}"
)

if [ -z "${CONNECTION_STRINGS[${LIMESURVEY_DB_TYPE}]}" ]; then
    logError "error: invalid database type: ${LIMESURVEY_DB_TYPE}" >&2
    logError "  LIMESURVEY_DB_TYPE must be either \"mysql\", \"dblib\", \"pgsql\" or \"sqlsrv\"." >&2
    exit 1
fi


####################################################################
######################## Download LimeSurvey #######################

if [ ! -f "${APP_DIR}/.RELEASE_${LIMESURVEY_GIT_RELEASE}" ] || isYes "${LIMESURVEY_FORCE_FETCH}"; then
    find "$APP_DIR" -maxdepth 1 -type f -name '.RELEASE_*' -delete

    logInfo "Retrieving LimeSurvey... (this operation may take a while)" >&2
    wget -O "/tmp/lime.tar.gz" \
        --progress="$( [ -t 1 ] && echo 'bar:noscroll' || echo 'dot:mega' )" \
        "https://github.com/LimeSurvey/LimeSurvey/archive/${LIMESURVEY_GIT_RELEASE}.tar.gz"


    logInfo "Extracting files from archive..." >&2
    tar -xzf "/tmp/lime.tar.gz" \
        --strip-components=1 \
        --keep-newer-files \
        --exclude-vcs \
        --to-command='sh -c '\''
            mkdir -p "$(dirname "'"${APP_DIR}"'/$TAR_FILENAME")" &&
                touch "'"${APP_DIR}"'/$TAR_FILENAME" &&
                dd of="'"${APP_DIR}"'/$TAR_FILENAME" >/dev/null 2>&1 &&
                echo "'"${APP_DIR}"'/$TAR_FILENAME" '\' |
        xargs -I '{}' touch -t 195001010000 '{}'

    chown -R "${APPLICATION_USER}:${APPLICATION_GROUP}" "$APP_DIR"
    rm "/tmp/lime.tar.gz"

    touch ".RELEASE_${LIMESURVEY_GIT_RELEASE}"
fi


####################################################################
######################### LimeSurvey Setup #########################

# Install BaltimoreCyberTrustRoot.crt.pem
if [ ! -f "${APP_DIR}/BaltimoreCyberTrustRoot.crt.pem" ]; then
    logInfo "Downloading BaltimoreCyberTrustroot.crt.pem..."
    curl -fsSLo "${APP_DIR}/BaltimoreCyberTrustRoot.crt.pem" \
        "https://www.digicert.com/CACerts/BaltimoreCyberTrustRoot.crt.pem"
fi

if [ ! -f "${APP_DIR}/application/config/config.php" ]; then
    logWarn "No config file for LimeSurvey"
    logWarn "  Copying default config file..."
    # Copy default config file but also allow for the addition of attributes
    echo "            'attributes' => array()," |
        awk '/lime_/ && c == 0 { c = 1; system("cat") } { print }' \
            "${APP_DIR}/application/config/config-sample-${LIMESURVEY_DB_TYPE}.php" \
            > "${APP_DIR}/application/config/config.php"
fi

# Set LimeSurvey configs
setConfigLS -a 'db' -k 'connectionString' "'${CONNECTION_STRINGS[${LIMESURVEY_DB_TYPE}]}'"
setConfigLS -a 'db' -k 'tablePrefix' "'${LIMESURVEY_TABLE_PREFIX}'"
setConfigLS -a 'db' -k 'username' "'${LIMESURVEY_DB_USER}'"
setConfigLS -a 'db' -k 'password' "'${LIMESURVEY_DB_PASSWORD}'"
setConfigLS -a 'urlManager' -k 'urlFormat' "'path'"
setConfigLS -k 'debug' "${LIMESURVEY_DEBUG}"
setConfigLS -k 'debugsql' "${LIMESURVEY_SQL_DEBUG}"

if [ -n "${MYSQL_SSL_CA}" ]; then
    setConfigLS -a 'db' 'attributes' \
        "array(PDO::MYSQL_ATTR_SSL_CA => '${APP_DIR//\//\\\/}\/${MYSQL_SSL_CA}',
            PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => false)"
fi

declare cfg key val
for ENV_VAR in $(envListVars "limesurvey\."); do
        val="$(envGetValue "$ENV_VAR")"
        cfg="${ENV_VAR#limesurvey.}"
        cfg="${cfg%%.*}"
        key="${ENV_VAR#limesurvey.*.}"
        setConfigLS -a "$cfg" "$key" "$val"
done

mkdir -p "${APP_DIR}/upload/surveys"
chown -R "${APPLICATION_USER}:${APPLICATION_GROUP}" \
    "${APP_DIR}/tmp" "${APP_DIR}/upload" "${APP_DIR}/application/config"

####################################################################
#################### LimeSurvey Database Setup #####################

if [ -n "${LIMESURVEY_USE_INNODB}" ]; then
    # If you want to use INNODB - remove MyISAM specification from LimeSurvey code
    sed -i "/ENGINE=MyISAM/s/\(ENGINE=MyISAM \)//1" \
        "${APP_DIR}/application/core/db/MysqlSchema.php"
fi

logInfo "Waiting for database..." >&2
while ! curl -sL "${LIMESURVEY_DB_HOST}:${LIMESURVEY_DB_PORT:-3306}"; do sleep 1; done

DBSTATUS=$(TERM=dumb php -f "$DB_SETUP_PHP" -- \
    "${LIMESURVEY_DB_HOST}" "${LIMESURVEY_DB_USER}" "${LIMESURVEY_DB_PASSWORD}" \
    "${LIMESURVEY_DB_NAME}" "${LIMESURVEY_TABLE_PREFIX}" "${MYSQL_SSL_CA}" \
    "${APP_DIR}") &>/dev/null

if [ "${DBSTATUS}" != "DBEXISTS" ] &&  [ -n "${LIMESURVEY_ADMIN_USER}" ] && [ -n "${LIMESURVEY_ADMIN_PASSWORD}" ]; then
    logInfo 'Database not yet populated - installing Limesurvey database' >&2
    su - "${APPLICATION_USER}" \
        -c php -f "${APP_DIR}/application/commands/console.php" -- \
            install "${LIMESURVEY_ADMIN_USER}" "${LIMESURVEY_ADMIN_PASSWORD}" \
            "${LIMESURVEY_ADMIN_NAME}" "${LIMESURVEY_ADMIN_EMAIL}" verbose
fi

if [ -f "${APP_DIR}/application/commands/UpdateDbCommand.php" ]; then
    logInfo 'Updating database...' >&2
    su - "${APPLICATION_USER}" -c php "${APP_DIR}/application/commands/console.php" updatedb
else
    logWarn 'WARNING: Manual database update may be required!' >&2
fi

if [ -n "${LIMESURVEY_ADMIN_USER}" ] && [ -n "${LIMESURVEY_ADMIN_PASSWORD}" ]; then
    logInfo 'Updating password for admin user...' >&2
    su - "${APPLICATION_USER}" \
        -c php -f "${APP_DIR}/application/commands/console.php" -- \
            resetpassword "${LIMESURVEY_ADMIN_USER}" "${LIMESURVEY_ADMIN_PASSWORD}"
fi

出力は次のとおりですbash --version

GNU bash, version 4.4.20(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

編集2

私は私ができることをすべてgithubに投稿しました。ここにいる犯罪

わかりませんが、リポジトリを複製して起動スクリプトを実行するとうまくいくと思います。

答え1

value、またはkey次の値は設定されませんarg

declare value key arg

したがって、key割り当てに達しなかった場合:case

while (( $# > 0 )); do
    arg="$1" && shift
    case "$arg" in
        --key=*)
            key="${arg#*=}"
        ;;

keyループ後もまだ設定解除(「バインド解除」)され、スクリプトに含まれているため、これを使用するset -uとエラーが発生します。

if [ -z "$key" ]; then # line 66: key: unbound variable

のように変数を空の文字列に初期化するとdeclare key= value= arg=問題が解決します。

ただし、以下を参照することもできますargs

if [ -z "$key" ]; then # line 66: key: unbound variable
    if (( ${#args} > 0 )); then

これは、配列内の要素の数ではなく、args配列args[@]のゼロ番目の要素の長さを取得することを意味します。argsただし、args空の場合はゼロ番目の要素が存在せず、再びエラーが発生します。

関連情報