拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

devops-exercises 仓库实战解析:用 Bash 算术展开与条件短路实现“因子检测“脚本(Factors)

devops-exercises 仓库实战解析:用 Bash 算术展开与条件短路实现“因子检测“脚本(Factors)
  • 文档
  • 教程
  • DevOps
  • 运维

【免费下载链接】devops-exercises

Linux, Jenkins, AWS, SRE, Prometheus, Docker, Python, Ansible, Git, Kubernetes, Terraform, OpenStack, SQL, NoSQL, Azure, GCP, DNS, Elastic, Network, Virtualization. DevOps Interview Questions

项目地址:https://gitcode.com/GitHub_Trending/de/devops-exercises
点击查看免费下载

导读

factors.md是 devops-exercises 仓库中 Shell Scripting 练习系列(位于 topics/shell/factors.md)的算术(Arithmetic)主题题目,其官方参考解在 topics/shell/solutions/factors.md。题目要求:给定一个数字,检测其是否以 2 或 3 为因子,并输出对应提示文本;当 2、3 均不是因子时则原样打印该数字本身。本篇将逐行拆解这份只有 4 行核心代码的精巧解法,讲透 Bash 算术展开((...))、&&/||短路求值、+=字符串拼接以及${var:-default}参数展开等关键语法,并结合仓库中其他同系列练习与实测输出,帮助你彻底掌握这一类"用 Bash 表达式代替 if 语句"的写法,可复制、可运行、可直接迁移到日常脚本中。

一、练习目标:三句话讲清需求

题目原文(topics/shell/factors.md)明确要求编写一个脚本,当给它传入一个数字时:

  1. 检测该数字是否有因子 2,若有则打印one factor;
  2. 检测该数字是否有因子 3,若有则打印one factor...actually two!;
  3. 若 2 和 3 都不是它的因子,则打印该数字本身。

注意需求是"叠加"而非"互斥":一个数字可能同时被 2 和 3 整除(例如 6、12),此时两行提示应同时成立,最终输出合并结果。整个练习在 topics/shell/README.md 的练习索引表中被归类为Arithmetic(算术)主题,难度标注为Basic(基础),与 Variables、Conditionals、For Loops、Functions 等练习共同构成仓库的 Shell 脚本入门训练链。

二、官方参考解:四行代码的精妙之处

仓库给出的参考解全文如下(topics/shell/solutions/factors.md):

#!/usr/bin/env bash (( $1 % 2 )) || res="one factor" (( $1 % 3 )) || res+="...actually two!" echo ${res:-$1}

去掉 shebang 后真正参与逻辑的只有 3 行。要把这份解法真正"读懂、能复述、能举一反三",需要先理解它背后四个语法点,下面逐一展开。

1. shebang:为什么是#!/usr/bin/env bash

脚本首行#!/usr/bin/env bash是 shebang。仓库 topics/shell/README.md 的自测题中专门解释过:/bin/bash是 Linux 系统最常见的登录默认 shell,Bash(Bourne-again shell)功能丰富、语法完善、可执行绝大多数脚本,因此被广泛使用。而通过env间接查找bash的好处是:不硬编码/bin/bash的绝对路径,在 bash 安装于非标准位置(如/usr/local/bin/bash、或通过 Homebrew、conda 等安装)的系统上也能正确解析解释器,提高脚本的可移植性。

2. 算术展开((...)):$(( )) 的兄弟语法

Bash 支持两种算术求值写法:

  • 命令替换形式$(( 表达式 )):求值后把结果作为"值"替换出来,常用于赋值或输出,例如仓库 README 中给出的$(( 1 + 2 )),以及 sum.md 里的echo $(( $1 + $2 ));
  • 命令形式(( 表达式 )):不输出结果,只对表达式求值并产生退出状态码——表达式值非 0 时状态为 1(失败),值为 0 时状态为 0(成功)。

factors 解法正是利用了命令形式的这一特性。(( $1 % 2 ))中%是取模运算符:当$1能被 2 整除时,$1 % 2等于 0,整条命令返回状态 0(成功);不能被 2 整除时返回非 0(失败)。这与 README 中另一道自测题的写法if [ $(($1 % 4)) -eq 0 ]; then在数学上是同一件事——判断n % k == 0等价于"k 是 n 的因子"——只是这里把 if 替换成了短路表达式。

3.||与&&的短路求值:把 if 写进一行

Bash 中cmd1 || cmd2表示"若 cmd1 失败(非 0 状态)则执行 cmd2",cmd1 && cmd2表示"若 cmd1 成功(状态 0)则执行 cmd2",二者都具备短路特性:cmd2 是否执行由 cmd1 的退出状态决定。

回到解法第一行(( $1 % 2 )) || res="one factor":

  • 当$1不能被 2 整除时,((...))返回失败,||右侧的赋值不执行,res保持未设置;
  • 当$1能被 2 整除时,((...))返回成功,||短路,||右侧的赋值也不执行!

等等——这岂不是反了?这里正是本解法最容易让初学者困惑的"反直觉"点:res="one factor"恰恰是在能被整除时需要设置的。要解开这个矛盾,关键在于第三点——注意区分(( 1 ))与(( 0 )):(( 表达式 ))的退出状态规则是"表达式值为 0 时返回失败,非 0 时返回成功",与 C 语言逻辑相反。因此:

$1取值$1 % 2(( $1 % 2 ))退出状态||结果res最终值
40失败(1)执行右侧赋值one factor
71成功(0)跳过右侧赋值未设置

也就是说:(( 取模结果 ))的失败状态恰好等于"取模结果为 0",即"能整除"。解法巧妙地用"取模命令失败"来代表"因子成立",从而让||在正确条件下触发赋值。这种写法在 argument_check.md 的[[ ${1} == "pizza" ]] && echo ... || echo ...和 README 中的三元写法[[ $a = 1 ]] && b="yes, equal" || b="nope"里是一致的思路——用短路表达式替代 if/else。

4.+=拼接与${res:-$1}参数展开

第二行(( $1 % 3 )) || res+="...actually two!":

  • res+=字符串是 Bash 的追加赋值运算符,等价于res="$res字符串"。当$1同时被 2 和 3 整除(如 6、12)时,res先被赋为one factor,再被追加为one factor...actually two!——这正是需求 1、2 叠加的体现;
  • 注意:若res此前未设置(即不能被 2 整除),+=会直接把它当作空串处理,得到...actually two!,输出语义上略显怪异的...actually two!。这是参考解的一个已知行为边界:它把"有因子 3"与"因子 2 不存在"两种情况都视为可接受输出。后文将给出一个修正版本。

第三行echo ${res:-$1}用到 Bash 参数展开${var:-default}:当变量var未设置或为空时,展开结果为default;否则展开结果为var的值(注意:-不会真正修改变量本身)。这正好覆盖需求 3:当res从未被赋值(2、3 都不是因子,如 5、7)时,${res:-$1}展开为$1本身,脚本打印输入的数字;否则打印累积的因子提示文本。

三、运行验证:把参考解跑起来

将参考解保存为factors.sh并赋予执行权限(chmod +x factors.sh),或直接bash factors.sh <数字>运行。以 Bash 实测(仓库仓库当前环境为 Linux 默认 bash)结果如下:

输入预期输出实测输出解释
4one factorone factor4 % 2 == 0,仅命中因子 2
9...actually two!...actually two!9 % 3 == 0,仅命中因子 3,res前缀为空
6one factor...actually two!one factor...actually two!同时被 2、3 整除,拼接生效
12one factor...actually two!one factor...actually two!同上
5552、3 均非因子,${res:-$1}回退到数字本身
777同上

实测同时印证了第二节的逐行分析:6 与 12 走的是"赋值 + 追加"路径,5 与 7 走的是"参数展开回退"路径,4 与 9 则分别只命中一个因子。你也可以用bash -x factors.sh 6打开 xtrace 逐步观察每条命令的退出状态与变量变化,这与仓库 topics/shell/README.md 中推荐的-x调试手段一致。

说明:(( 表达式 ))的退出状态规则(值为 0 返回失败、非 0 返回成功)是 Bash 的既定语义,并非本项目自定义行为;上述实测输出与逐条推演完全吻合,可作为验证依据。

四、进阶辨析:参考解的边界与改进

参考解胜在极简,但有两个值得讨论的边界,理解它们能帮你写出更稳健的版本。

边界 1:输入校验缺失

脚本直接使用$1,未校验参数是否存在、是否为整数。参照仓库其他练习的校验惯例:

  • sum.md 用正则re='^[0-9]+$'配合! [[ $1 =~ $re && $2 =~ $re ]]校验两个入参必须是数字;
  • num_of_args.md 用set -eu+case $# in ...)处理 0 个、1 个、多个参数三种情形;
  • directories_comparison.md 用test $# -ne 2校验参数个数。

参考解的(( $1 % 2 ))在无参数或传入非整数时会报错((( ))把空串/非法内容按 0 处理,无参数时直接得到"能被 2 整除"从而输出one factor,行为不符合直觉)。因此生产环境建议先加参数个数与数字校验。

边界 2:单因子输出前缀问题

当数字仅被 3 整除(如 9)时,参考解输出...actually two!而非one factor...actually two!,因为res在第一步未被赋值、+=从空串开始。若希望语义更严谨,可改为先初始化空值再拼接,或显式区分两种情况:

#!/usr/bin/env bash # 更严谨的版本:仅被 3 整除时输出完整的 "one factor...actually two!" res="" (( $1 % 2 )) || res="one factor" if (( $1 % 3 == 0 )); then res+="...actually two!" fi echo "${res:-$1}"

其中(( $1 % 3 == 0 ))是更直观的写法:判断"取模结果为 0",成功状态即代表"3 是因子",语义与 README 中的if [ $(($1 % 4)) -eq 0 ]完全一致。此版本同样实测通过,可作为对比学习材料。

五、把它放回训练体系中:这份练习的位置与价值

在 topics/shell/README.md 的练习索引表中,Factors 位列第 4 行,Topic 为 Arithmetic、难度为 Basic,位于 Hello World、Basic date(变量)与 Argument Check(条件判断)之间,前后呼应:

  • 前置铺垫:变量赋值(HW_STR="Hello World"、echo $(date))、位置参数$1的读取(README 自测题echo $1);
  • 同主题对照:算术运算的两种形态$(( 1 + 2 ))与expr 1 + 2,以及因子判断if [ $(($1 % 4)) -eq 0 ];
  • 后续进阶:Conditionals(Argument Check 的[[ ]]+&&/||)、Loops(Count Chars 的 while)、Functions(Sum)以及参数处理(num_of_args 的case)。

可以看出,Factors 练习是仓库刻意安排的"以最少语法点覆盖最多核心机制"的一题:它在一个脚本内同时训练了算术展开、取模运算、短路求值、追加赋值、参数展开与默认值回退。掌握它之后,再看仓库中的echo "${1:-yay}"(README 变量自测题)、echo "Today is ${1:-a great day!}"(great_day.md)等默认值展开用法会毫无障碍。

六、总结

语法点在本练习中的用法仓库中的旁证
#!/usr/bin/env bash可移植地指定 Bash 解释器topics/shell/README.md shebang 自测题
(( 表达式 ))取模求值,用退出状态表达"能否整除"README 中$(( 1 + 2 ))、if [ $(($1 % 4)) -eq 0 ]
||短路整除时触发赋值argument_check.md、README 三元写法
+=多因子提示文本拼接—(Bash 通用语法)
${var:-default}无因子时回退打印数字README 中echo "${1:-yay}"、great_day.md

一句话概括:factors 参考解用"取模命令的失败状态"反向表达"整除成立",再借||与${var:-default}把三分支的 if/else 压缩成三行表达式,是理解 Bash 控制流"表达式化"写法的绝佳入门样例。建议读者对照 topics/shell/factors.md 重新自测,再尝试自行实现带输入校验与完整输出的增强版,把本节知识点内化为自己的脚本技能。

  • 文档
  • 教程
  • DevOps
  • 运维

【免费下载链接】devops-exercises

Linux, Jenkins, AWS, SRE, Prometheus, Docker, Python, Ansible, Git, Kubernetes, Terraform, OpenStack, SQL, NoSQL, Azure, GCP, DNS, Elastic, Network, Virtualization. DevOps Interview Questions

项目地址:https://gitcode.com/GitHub_Trending/de/devops-exercises
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

返回列表