Bash中; && ||命令连接符不是逻辑连接符、多个命令的执行顺序

Bash中; && ||命令连接符不是逻辑连接符、多个命令的执行顺序

ShellBash语言中,; && ||三个符号用于分隔命令,根据前一个命令的执行状态,按顺序执行后面的命令。它们形似逻辑运算符,但其实不是逻辑运算符,而是命令连接符

基础知识

  • 【重要】每个命令执行后都有进程退出状态码,下面简称退出状态码(Exit Status)。为0则表示命令成功执行。非0则表示命令执行失败。具体是多少,由该命令对退出状态码指定。

  • echo命令没有特殊情况,退出状态码通常是0(因为几乎总是成功执行)。

  • ;(分号)用于分隔多个命令,按顺序执行这些命令,无论前一个命令执行成功与否。如在命令行中敲入echo "Hello"; echo "World",按下回车,会输出Hello,同时换行输出World

  • &&(AND连接符)分隔多个命令,只有前一个命令退出状态码为零即执行成功),就会执行下一条命令。

  • ||(OR连接符)分隔多个命令,只有前一个命令退出状态码非零即执行失败),才会执行下一条命令。

  • 注意:&&||作用的范围是下一条命令,不是整行命令;如果有一行命令序列中有多条命令,如果下一条命令不能执行,不代表下下条命令不能执行。

  • 如果退出状态码非零(即执行失败),在命令行中会打印错误信息,但并不意味着整行命令停止运行。

没有优先级

  • Bash语言中,作为命令连接符; && ||并没有优先级
  • 具体到执行上,如果没有括号,永远是从左往右执行
  • 但是&& ||在退出状态码上,&&||有逻辑连接符的特征:A&&B时,只有AB退出状态码都为0,整体的退出状态码才是0。A||B时,如果AB的退出状态码有一个是0,整体的退出状态码就是0。
  • 学过Java的同学会有认识:(2>5 && 123<345 && 123*345==42435),在Java中,当走到2>5时,就可以忽略后面两条直接判定false了;在shell命令中,(A&&B&&C)也一样,只要A出错了,整体的退出状态码就是非0。(也可类比(A||B||C)

命令序列的执行顺序

在判断某一行由许多命令由命令连接符组成的命令序列的执行顺序时,我们需要如下操作:(请结合例子动笔写下关键的推理过程)

  1. 有括号优先处理括号。

  2. 然后从左往右看,每次执行命令后,留意退出状态码与接下类的命令连接符

  3. 满足以下条件之一的,继续执行下面的命令,返回第2条:

    • 命令连接符是;
    • 命令连接符是&&且退出状态码为0
    • 命令连接符是||且退出状态码为非0

    满足以下条件之一的,执行第4条:

    • 命令连接符是&&且退出状态码为非0
    • 命令连接符是||且退出状态码为0
  4. 将命令连接符连起来的两条命令外围扩上一层小括号,例如(A ; B)/(A && B)/(A || B),判断小括号内的退出状态码

    • 如果最终B不执行,返回A的退出状态码
    • 如果最终B执行了,返回B的退出状态码
    • 说人话:B不执行,只要A失败了,整条命令就失败了;B执行了,管他A怎么鸟样,只要B失败了,整条命令就失败了。其余情况整条命令都是成功的。
  5. 然后返回第2条,直到所有的命令都执行完毕。

例如:下面的例子中,很多人会误以为不会输出任何东西,但实际上会输出bb

true || echo aa && echo bb

分析:

  • true不会执行任何操作,返回退出状态码0。后方命令连接符是||,所以echo aa不会执行。
  • 然后分析(true || echo aa)&& echo bb,因为echo aa不执行,所以返回true这条命令的退出状态码。因为退出状态码为0,所以(true || echo aa)整条命令的退出状态码为0
  • 然后分析&& echo bb,因为&&且退出状态码为0,所以执行echo bb,打印bb,退出状态码还是0