我在 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
in 的分配: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
为空,则第零个元素不存在,再次出现错误。