严格模式帮助shell脚本调试

neevop 十月 8, 2022

起因

说起shell,我愿意称之为上天的恩赐。本篇文章介绍shell脚本的严格模式,带你解读脚本开始那看不懂的几行set。严格模式中对于单行命令执行结果的检查对我们调试脚本会有很大的帮助,同时我们可以使用一些小技巧实现类似高级语言调试的效果。 几乎所有为人称赞的shell项目中都会灵活的使用到set +/-e的操作,当我们需要去模仿源码的时候,会造成一定的困扰。当然掌握这些技巧可以提升编码的内功。

# https://github.com/ohmyzsh/ohmyzsh/blob/master/tools/require_tool.sh
__require_tool_version_compare ()
{
  (
    # Locally ignore failures, otherwise we'll exit whenever $1 and $2
    # are not equal!
    set +e

awk_strverscmp='
...

# https://github.com/ohmyzsh/ohmyzsh/blob/master/tools/install.sh
...
set -e

# Make sure important variables exist if not already defined
#
# $USER is defined by login(1) which is not always executed (e.g. containers)
# POSIX: https://pubs.opengroup.org/onlinepubs/009695299/utilities/id.html
USER=${USER:-$(id -u -n)}
...
# https://github.com/nvm-sh/nvm/blob/master/update_test_mocks.sh
#!/usr/bin/env bash

set -e

echo 'Updating test mocks...'

MOCKS_DIR="$PWD/test/fast/Unit tests/mocks"
...

谷歌上关于Shell脚本的严格模式介绍大多是出自**Use Bash Strict Mode **这篇文章。毫无意外,本文的大大部分观点也是基于这篇文章的。之所以重新写一篇文章记录这个内容,主要是因为该作者所说的非官方用法比较魔法,不太接地气,凡人只需要使用其中的一小点就够了。

预备知识:

  1. 关于shell命令的返回值
  2. {}代码块在shell中的应用

默认状态:

默认情况下,如果您在命令行上使用 bash,则此默认行为正是您想要的 - 您不希望拼写错误将您注销!但是在脚本中,你真的想要相反的东西。如果脚本中的一行失败,但最后一行成功,则整个脚本都有一个成功的退出代码。这使得很容易错过错误。

strict mode

  1. set -e

    脚本内任一行具有非零退出状态,该set -e选项指示 bash 立即退出。你不想为你的命令行 shell 设置它,但在脚本中它非常有用。在所有广泛使用的通用编程语言中,未处理的运行时错误——无论是 Java 中抛出的异常,还是 C 中的分段错误,或 Python 中的语法错误——都会立即停止程序的执行;不执行后续行。

  2. set +e

    我们使用了与“set -e”相反的“set +e”。每当“set -e”强制代码终止执行时,只要遇到非零值,相反的就会反对它。“set +e”在前两个函数的函数调用之前声明,“set -e”在最后两个方法的函数调用之前声明。


    其实到这里就已经可以大致使用严格模式了,后续部分笔记收藏。


  3. set -u

    设置后,对您之前未定义的任何变量的引用 - 除了$*$@- 是一个错误,并导致程序立即退出。出于各种充分的理由,Python、C、Java 等语言的行为方式都相同。一个是错别字不会在您没有意识到的情况下创建新变量。

  4. set -o

    此设置可防止管道中的错误被屏蔽。如果管道中的任何命令失败,则该返回码将用作整个管道的返回码。默认情况下,管道的返回码是最后一条命令的返回码——即使它成功了。

使用避坑

后怕