Bash 最佳实践和技巧

简介: Bash 是系统编程的 JavaScript. 但有时使用系统编程语言如 C 或 Go 会更好, Bash 是理想的面向 POSIX 标准的小任务或命令行级系统编程语言

progrium/bashstyle (中文翻译 yujiaao)


Bash 是系统编程的 JavaScript. 但有时使用系统编程语言如 C 或 Go 会更好, Bash 是理想的面向 POSIX 标准的小任务或命令行级系统编程语言, 下面是三个显见的原因:

  • 它无处不在. 尤如 JavaScript 对 web 而言, Bash 在这里并为系统编程已准备好.
  • 它是中性的. 不同于 Ruby, Python, JavaScript, 或 PHP, Bash 平等地得罪了全部社群. ;)
  • 它生为胶水. 复杂部分用 C 或 Go (或其他什么语言!)来写, 然后用 Bash 把他们粘在一块.

本文档描述了我怎么写 Bash 以及我希望如何与合作者一起在我的开源项目里写 Bash. 本文基于大量经验和花了大量时间收集最佳实践. 多数来自这

两篇

文章,

但此处集成, 略有修改, 并聚焦在最值钱的点上. 加上一些新东西!

记住本文不是讲一般的 shell 脚本编程, 这里是专门针对 Bash 和 Bash 作为解释器的规则.


大规则

  • 总是用双引号括起变量, 包括子shell. 不要裸体的$符号.
  • 全部代码进函数. 即使只有一个函数,main.
  • 除非是库脚本, 你可以全局进行脚本设置并调用 main. 如此而已.
  • 避免使用全局变量. 如定义常量则使用 readonly
  • 可执行脚本总有main函数, 调用时用mainmain "$@"
  • 如果脚本也用作库, 调用时用 [[ "$0" == "$BASH_SOURCE" ]] && main "$@" 判断
  • 设置变量时总是用local修饰, 除非有理由用declare
  • 少数例外情况是当你故意想在外层空间设置一个变量.
  • 变量都用小写, 除非想输出到环境中.
  • 总用set -eo pipefail. 快速失败并检查退出状态(exit codes).
  • 在那些你有意让退出状态不为零的程序里使用 || true .
  • 切勿使用弃用的样式. 最值得注意的:
  • 函数定义成 myfunc() { ... }, 而不是 function myfunc { ... }
  • 判断条件总是用 [[ 替代 [test
  • 从不使用反引号, 使用 $( ... )
  • 更多详见 http://wiki.bash-hackers.org/...
  • 优先使用绝对路径 (借助 $PWD), 总用 ./ 来修饰相对路径.
  • 两行以上的函数总是在顶部用declare声名变量和为参数命名
  • 如: declare arg1="$1" arg2="$2"
  • 定义可变参数函数时例外。见下文.
  • 使用 mktemp 创建临时文件, 总是用 trap 来清理.
  • 警告和错误应该发送到 STDERR, 任何可解析的内容都应该转到 STDOUT.
  • 完成后尝试本地化 shopt 使用和禁用选项.

如果你知道你在做什么,你可以曲解或破坏其中的一些规则,但通常它们是正确的并且非常有帮助。


最佳实践和技巧

  • 如果可能,请在 awk/sed 之前使用 Bash 变量替换.
  • 通常使用双引号,除非使用单引号更有意义.
  • 对于简单的条件,请尝试使用 &&||.
  • 不要害怕 printf, 它比 echo 更强大.
  • then, do, 等入在同一行, 不换行.
  • 如果您可以测试退出代码, 请避免在 if 表达式中中使用 [[ ... ]].
  • 如果要包含/源码执行文件请使用 .sh.bash 扩展名. 但不用在可执行脚本上.
  • 把复杂的单行 sed, perl命令等放在一个独立的函数中, 且使用描述性名称.
  • 包含 [[ "$TRACE" ]] && set -x 是个好主意.
  • 做简单明了的设计.
  • 避免使用选项标志和解析,而是尝试使用可选的环境变量.
  • 将子命令用于必要的不同“模式”.
  • 在大型系统或任何CLI命令中,向函数添加描述.
  • 在函数顶部使用 declare desc="description",甚至在参数声明之上.
  • 这可以使用反射查询/提取。例如:

eval $(type FUNCTION_NAME | grep 'declare desc=') && echo"$desc"

  • 意识到需要可移植性。在容器中运行 Bash 可以比 Bash 在多个平台上运行做出更多的假设.
  • 在期望或导出环境变量时,考虑可能涉及子 shell 的命名空间变量.
  • 使用硬tab符。Heredocs忽略了前导tab符,允许更好的缩进.


好的参考和帮助


例子

具有命名参数的常规函数

使用参数定义函数

regular_func() {
    declare arg1="$1" arg2="$2" arg3="$3"
    # ...
}

变参函数

使用最终的可变参数定义函数

variadic_func() {
    local arg1="$1"; shift
    local arg2="$1"; shift
    local rest="$@"
    # ...
}

条件:测试退出代码与输出

# 测试退出代码(-q静音输出)
if grep -q 'foo' somefile; then
  ...
fi
# 测试输出( -m1 限制一个结果)
if [[ "$(grep -m1 'foo' somefile)" ]]; then
  ...
fi
相关文章
|
4月前
|
Linux Shell Windows
4:Bash shell命令-步入Linux的现代方法
4:Bash shell命令-步入Linux的现代方法
53 0
|
6月前
|
关系型数据库 MySQL Shell
【Linux命令】-bash: mysql: command not found
【Linux命令】-bash: mysql: command not found
56 0
|
8月前
|
Ubuntu 安全 Linux
不用安装虚拟机,直接在Windows上面运行Linux Bash Shell,嗯!真香!!!
不用安装虚拟机,直接在Windows上面运行Linux Bash Shell,嗯!真香!!!
153 0
|
2天前
|
Java Shell Linux
【linux进程控制(三)】进程程序替换--如何自己实现一个bash解释器?
【linux进程控制(三)】进程程序替换--如何自己实现一个bash解释器?
|
16天前
|
存储 Shell Linux
【攻防世界】unseping (反序列化与Linux bash shell)
【攻防世界】unseping (反序列化与Linux bash shell)
|
2月前
|
Linux Shell
mac/linux提示bash: telnet: command not found
mac/linux提示bash: telnet: command not found
|
9月前
|
Kubernetes Shell Linux
linux中sh/bash 进程为何不接受kill SIGTERM
在k8s的优雅终止中,如果容器的入口进程是sh 或者bash,需要注意你设定的gracefulterminate是无效的
106 0
|
10月前
|
Java Shell Linux
如何在 Linux 中使用 Bash For 循环
如何在 Linux 中使用 Bash For 循环
90 0
|
10月前
|
Unix Shell Linux
Linux中sh与bash的区别(详细介绍)
Linux中sh与bash的区别(详细介绍)
320 0
|
11月前
|
Shell Linux
Linux pip命令报错 -bash: pip: command not found
Linux pip命令报错 -bash: pip: command not found
255 0