当APP_ENV
“prod”DATABASE_DISABLE_MIGRATIONS
为空时;在任何其他情况下,它应该是true
除非 DATABASE_DISABLE_MIGRATIONS
为空或 null。换句话说,其价值必须得到尊重并且只有当它是时才应进行自动分配未设置。
原来的DATABASE_DISABLE_MIGRATIONS |
APP_ENV |
新的DATABASE_DISABLE_MIGRATIONS |
---|---|---|
空或空 | 任何 | 保留原有价值 |
非零长度字符串 | 任何 | 保留原有价值 |
未设置 | “产品” | 空的 |
未设置 | 空或不是“prod” | “真的” |
我正在开始学习 shell (POSIX),并且我已经使用以下代码成功完成了此任务:
#!/bin/sh
if [ "${DATABASE_DISABLE_MIGRATIONS+set}" != set ]; then
: ${DATABASE_DISABLE_MIGRATIONS:=$( [ "$APP_ENV" = "prod" ] || echo "true" )}
fi
# Just for debug (not meant to be in the single-line assignment of course)
if [ -z "$DATABASE_DISABLE_MIGRATIONS" ]; then
echo "Empty value, migrations ENABLED"
else
echo "Not empty value, migrations DISABLED"
fi
可以将上面的代码写成单行语句吗?
当考虑未设置、空或空字符串时,很容易制作单行语句,我经常最终使用:
: ${FOO:=$( [ "$APP_ENV" = "prod" ] || echo "true" )}
变量在未设置、null 或空且不是“prod”时FOO
获取值。true
FOO
APP_ENV
也许我缺少类似的“捷径”?
答案1
使用 2 个变量的 case 语句可能就是您所追求的:
case "${DATABASE_DISABLE_MIGRATIONS}:${APP}" in
?*:* | "":prod) # not empty, or prod
echo "Not empty value, migrations DISABLED"
;;
*) echo "Empty value, migrations ENABLED"
;;
esac
虽然这并没有区分“空”和“未设置”。如果变量值之一包含冒号,也可能会被欺骗。
基于
原来的DATABASE_DISABLE_MIGRATIONS |
APP_ENV |
新的DATABASE_DISABLE_MIGRATIONS |
---|---|---|
空或空 | 任何 | 保留原有价值 |
非零长度字符串 | 任何 | 保留原有价值 |
未设置 | “产品” | 空的 |
未设置 | 空或不是“prod” | “真的” |
不将自己限制在 POSIX 上,并且因为“未设置”是调整变量,我会:
if [[ ! -v DATABASE_DISABLE_MIGRATIONS ]]; then
if [[ $APP == prod ]]; then
DATABASE_DISABLE_MIGRATIONS=""
else
DATABASE_DISABLE_MIGRATIONS=true
fi
fi
这显然不能满足您的单行愿望,但我认为这是传达您的意图的最易读的方式。
答案2
命令:
[ -z "${DATABASE_DISABLE_MIGRATIONS+x}" ] && [ "$APP_ENV" != "prod" ] && DATABASE_DISABLE_MIGRATIONS=true
解释:
A评论指出将使用此信息检查变量。
[ -z "$DATABASE_DISABLE_MIGRATIONS" ]
此信息应该是问题的一部分。
"$DATABASE_DISABLE_MIGRATIONS"
除非使用set -u
(或),否则对于未设置的变量或空字符串,扩展的结果是相同的set -o nounset
,因此可以简化要求。不要用显式的空值替换未设置的变量,以防万一"prod"
您可以将其保留为未设置,并且您将获得相同的测试结果。
所以算法可以简化为
- 如果( DATABASE_DISABLE_MIGRATIONS 未设置且 APP_ENV 不是“prod”)将 DATABASE_DISABLE_MIGRATIONS 设置为“true”
- (否则什么也不做)
当然,您可以将其写成多行,这将使代码更清晰,但这不是一行,也不会比您在问题中的解决方案短多少。
if [ -z "${DATABASE_DISABLE_MIGRATIONS+x}" ] && [ "$APP_ENV" != "prod" ]
then
DATABASE_DISABLE_MIGRATIONS=true
fi
答案3
那么,如果变量未设置,那么您想将其设置为默认值,否则保留原始值? (这里的默认值取决于另一个变量,但我们不要让它分散注意力。)
也许最好这样做,然后:
# don't disable migrations in production by default
if [ "$APP_ENV" = prod ]; then
default_DATABASE_DISABLE_MIGRATIONS=
else
default_DATABASE_DISABLE_MIGRATIONS=true
fi
# set default value if unset
if [ "${DATABASE_DISABLE_MIGRATIONS+set}" != set ]; then
DATABASE_DISABLE_MIGRATIONS=$default_DATABASE_DISABLE_MIGRATIONS
fi
或者更短一点:
# don't disable migrations in production by default
default_DATABASE_DISABLE_MIGRATIONS=true
if [ "$APP_ENV" = prod ]; then
default_DATABASE_DISABLE_MIGRATIONS=
fi
# set default value if unset
: "${DATABASE_DISABLE_MIGRATIONS=$default_DATABASE_DISABLE_MIGRATIONS}"
基于相关值的代码高尔夫似乎会适得其反,因为如果您需要对其他值使用类似的逻辑,则需要修改整个代码。最好制定一个可以应用于其他类似案例的直接解决方案。尽管最后一行几乎是您在问题标题中所要求的,但仅当变量未设置时才进行单行 shell 分配。 ("${var:=default}"
它也适用于一组但为空的值。)
我还要指出,如果最终变量没有被否定,读者可能会更清楚。您提到使用 和 测试变量[ -z "$DATABASE_DISABLE_MIGRATIONS" ]
,基本上读起来像“如果不禁用迁移”。这与“如果启用迁移”相同,如果使用变量,则更容易编写$DATABASE_ENABLE_MIGRATIONS
。
答案4
可能:
case ${DATABASE_DISABLE_MIGRATIONS++}:$APP_ENV in
(:prod) DATABASE_DISABLE_MIGRATIONS= ;;
(:* ) DATABASE_DISABLE_MIGRATIONS=true;;
esac
就像任何代码一样,您可以将其放在一行上,只需将各行连接在一起(甚至压缩得有点像
case ${DATABASE_DISABLE_MIGRATIONS++}:$APP_ENV in(:prod)DATABASE_DISABLE_MIGRATIONS=;;(:*)DATABASE_DISABLE_MIGRATIONS=true;esac
)但我没有看到好处。
执行$(... echo ...)
意味着(ksh93 中除外)分叉一个额外的进程,运行echo
命令并通过管道发送和接收该命令,这意味着数十万甚至更多的 CPU 指令,而那些简单的 shell 分配和模式匹配只会有数百个,所以它我觉得有点傻。