函数内随机未绑定变量错误

函数内随机未绑定变量错误

我在 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

因此,如果未达到keyin 的分配:case

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

thenkey在循环之后仍然会被取消设置(“未绑定”),并且由于脚本具有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为空,则第零个元素不存在,再次出现错误。

相关内容