为了希格斯- 完整脚本可以在这里找到这里- 我编写了以下丑陋的(但完美运行的)逻辑来打印和格式化脚本的输出,部分原因只是为了弄清楚所有边缘情况,也是因为我没有看到任何其他选择。
...
if [[ $W -le 0 && $D -le 0 && $H -eq 1 ]]; then string="$H hour"
elif [[ $W -le 0 && $D -le 0 && $H -gt 1 ]]; then string="$H hours"
elif [[ $W -le 0 && $D -eq 1 && $H -le 0 ]]; then string="$D day"
elif [[ $W -le 0 && $D -eq 1 && $H -eq 1 ]]; then string="$D day and $H hour"
elif [[ $W -le 0 && $D -eq 1 && $H -gt 1 ]]; then string="$D day and $H hours"
elif [[ $W -le 0 && $D -gt 1 && $H -le 0 ]]; then string="$D days"
elif [[ $W -le 0 && $D -gt 1 && $H -eq 1 ]]; then string="$D days and $H hour"
elif [[ $W -le 0 && $D -gt 1 && $H -gt 1 ]]; then string="$D days and $H hours"
elif [[ $W -eq 1 && $D -le 0 && $H -le 0 ]]; then string="$W week"
elif [[ $W -eq 1 && $D -le 0 && $H -eq 1 ]]; then string="$W week and $H hour"
elif [[ $W -eq 1 && $D -le 0 && $H -gt 1 ]]; then string="$W week and $H hours"
elif [[ $W -eq 1 && $D -eq 1 && $H -le 0 ]]; then string="$W week and $D day"
elif [[ $W -eq 1 && $D -gt 1 && $H -le 0 ]]; then string="$W week and $D days"
elif [[ $W -eq 1 && $D -eq 1 && $H -eq 1 ]]; then string="$W week, $D day and $H hour"
elif [[ $W -eq 1 && $D -eq 1 && $H -gt 1 ]]; then string="$W week, $D day and $H hours"
elif [[ $W -eq 1 && $D -gt 1 && $H -eq 1 ]]; then string="$W week, $D days and $H hour"
elif [[ $W -eq 1 && $D -gt 1 && $H -gt 1 ]]; then string="$W week, $D days and $H hours"
elif [[ $W -gt 1 && $D -le 0 && $H -le 0 ]]; then string="$W weeks"
elif [[ $W -gt 1 && $D -le 0 && $H -eq 1 ]]; then string="$W weeks and $H hour"
elif [[ $W -gt 1 && $D -le 0 && $H -gt 1 ]]; then string="$W weeks and $H hours"
elif [[ $W -gt 1 && $D -eq 1 && $H -le 0 ]]; then string="$W weeks and $D day"
elif [[ $W -gt 1 && $D -gt 1 && $H -le 0 ]]; then string="$W weeks and $D days"
elif [[ $W -gt 1 && $D -eq 1 && $H -eq 1 ]]; then string="$W weeks, $D day and $H hour"
elif [[ $W -gt 1 && $D -eq 1 && $H -gt 1 ]]; then string="$W weeks, $D day and $H hours"
elif [[ $W -gt 1 && $D -gt 1 && $H -eq 1 ]]; then string="$W weeks, $D days and $H hour"
elif [[ $W -gt 1 && $D -gt 1 && $H -gt 1 ]]; then string="$W weeks, $D days and $H hours"
fi
colour1='\033[0;31m'
colour2='\033[0;32m'
if (($elapsed < threshold))
then echo -e "${colour1}It's been $string since you last bought a $item."
else
echo -e "${colour2}It's been $string since you last bought a $item."
fi
也许我只是太笨了,但尽管上面的代码很尴尬,我还是想不出更好的重写方法。有更好的方法吗?如果有,那是什么?
答案1
在 bash 还没有表达式的时候[[ ]]
,案件语句经常用于模式匹配。 if 列表可以转换为单个 case 语句(符号+
只是任意分隔符):
case $W+$D+$H in
0+0+0 ) string="" ;;
0+0+1 ) string="$H hour" ;;
0+1+0 ) string="$D day" ;;
0+1+1 ) string="$D day and $H hour" ;;
1+0+0 ) string="$W week" ;;
1+0+1 ) string="$W week and $H hour" ;;
1+1+0 ) string="$W week and $D day" ;;
1+1+1 ) string="$W week, $D day and $H hour" ;;
0+0+* ) string="$H hours" ;;
0+*+0 ) string="$D days" ;;
*+0+0 ) string="$W weeks" ;;
0+1+* ) string="$D day and $H hours" ;;
0+*+1 ) string="$D days and $H hour" ;;
*+0+1 ) string="$W weeks and $H hour" ;;
*+1+0 ) string="$W weeks and $D day" ;;
1+0+* ) string="$W week and $H hours" ;;
1+*+0 ) string="$W week and $D days" ;;
1+1+* ) string="$W week, $D day and $H hours" ;;
1+*+1 ) string="$W week, $D days and $H hour" ;;
*+1+1 ) string="$W weeks, $D day and $H hour" ;;
0+*+* ) string="$D days and $H hours" ;;
1+*+* ) string="$W week, $D days and $H hours" ;;
*+0+* ) string="$W weeks and $H hours" ;;
*+*+0 ) string="$W weeks and $D days" ;;
*+1+* ) string="$W weeks, $D day and $H hours" ;;
*+*+1 ) string="$W weeks, $D days and $H hour" ;;
*+*+* ) string="$W weeks, $D days and $H hours" ;;
esac
这仍然很难理解,但如果我们将带有s
或不带有变量的复数单词分开,则该case
语句会简单得多:
ws=s; [ $W = 1 ] && ws=
ds=s; [ $D = 1 ] && ds=
hs=s; [ $H = 1 ] && hs=
case $W+$D+$H in
0+0+0 ) string="" ;;
0+0+* ) string="$H hour$hs" ;;
0+*+0 ) string="$D day$ds" ;;
0+*+* ) string="$D day$ds and $H hour$hs" ;;
*+0+0 ) string="$W week$ws" ;;
*+0+* ) string="$W week$ws and $H hour$hs" ;;
*+*+0 ) string="$W week$ws and $D day$ds" ;;
*+*+* ) string="$W week$ws, $D day$ds and $H hour$hs" ;;
esac
答案2
如果您在 bash 下运行该脚本,那么如何做呢(基于 Jeff Zeitlin 的评论):
# Start with an empty list of units
units=()
# Add the non-zero units to the list
if (( W == 1 )); then units+=("1 week")
elif (( W > 1 )); then units+=("$W weeks")
fi
if (( D == 1 )); then units+=("1 day")
elif (( D > 1 )); then units+=("$D days")
fi
if (( H == 1 )); then units+=("1 hour")
elif (( H > 1 )); then units+=("$H hours")
fi
# Based on the number of non-zero units, add separators appropriately
case ${#units[@]} in
3) string="${units[0]}, ${units[1]} and ${units[2]}" ;;
2) string="${units[0]} and ${units[1]}" ;;
1) string="${units[0]}" ;;
0) string="less than an hour" ;;
esac
警告:这几乎需要 bash。zsh 也有数组,但它对条目的编号不同(从 1 开始而不是 0),因此最后一部分在 zsh 下会奇怪地失败。
答案3
另一种方法是从冗长的版本开始,然后使用匹配模式将其修剪以删除复数等。但我不知道它是否更适合,因此可能更难维护。
string=" $W weeks, $D days and $H hours" # leading space to simplify matching
string=${string/ 1 weeks/1 week}
string=${string/ 1 days/ 1 day}
string=${string/ 1 hours/ 1 hour}
string=${string# 0 weeks,}
string=${string/ 0 days/}
string=${string%and 0 hours}
string=${string/, and/ and}
string=${string# }
string=${string% }
string=${string%,}
string=${string#and }
[[ $string =~ and ]] || string=${string/,/ and}
这使用 bash 的参数扩展语法,其中${parameter/pattern/replacement}
尝试匹配 glob 模式,如果找到则替换它。这用于“修复”像“1 周”这样的“错误”,使其变为“1 周”。为了避免也匹配21 weeks
,初始字符串在开头有一个额外的空格,因此空格可以位于模式中以确保仅匹配“ 1 ”。
如果在参数开头找到匹配项,此语法${parameter#pattern}
将删除该匹配项。这用于删除“0 周,”。
如果在参数末尾找到匹配项,此语法${parameter%pattern}
将删除该匹配项。这用于删除“and 0 hours”。其他修复方法是删除开头或结尾处的空格,或者删除结尾处的逗号,或者删除开头处的“and”和空格。
如果字符串中没有“and”,则最后一行将逗号替换为“and”。这相当于将“2 周,3 天”更改为“2 周和 3 天”,其中我们已经删除了“and 0 小时”。
答案4
猛击支持功能,所以也许可以尝试一个函数。我(用伪代码)勾勒出了一个可能采取的策略。
“s” 很容易处理,因为只有当值为 1 时它才应该是单数。逗号和“and”是基于计数的判断,因此一个函数返回一个字符串,要么是空字符串,要么是“正确复数化”,下一个函数用于根据字符串的空性质增加计数器。
然后使用计数器来操作两个分隔符字符串,最后将整个东西盲目地组装起来。
伪代码:
function foo ($int, $singular, $suffix, $ignoreZero) {
$rval = "";
if ( $ignoreZero && $int == 0 ) { return $rval; }
if ( $int == 1 ) { $rval = $int + " " + $singular; }
else { $rval = $int + " " + $singular + $suffix; }
return $rval;
}
function bar($int, $str) {
$rval = $int;
if ( $str !="" ) { $rval = $int + 1; }
return $rval;
}
$count = 0
$strW = foo($W, "week", "s", true);
$count = bar($count, $strW);
$strD = foo($D, "day", "s", true);
$count = bar($count, $strD);
$strH = foo($H, "hour", "s", true);
$count = bar($count, $strH);
$sep1 = "";
$sep2 = " and ";
if ($count == 3) { $sep1=", ";}
if ($count < 2) { $sep2=""; }
$emit = $strW + $sep1 + $strD + $sep2 + $strH