像专业人士一样编写 Bash 脚本 - 第一部分 - 样式指南
如果您不了解Bash 的“怪癖”和“特权”,编写脚本可能会很困难。在我的母语中,我们使用意第绪语中的“怪癖”和“特权”来表示;它被称为“Shtickim”( “Shtick”的复数形式)。您准备好了解更多关于 Bash 的“Shtickim”了吗?
这篇博文是我正在撰写的系列文章之一,目的是为将来容易忘事的我保存知识,帮助新同事,也为了满足像你这样渴望像我一样热爱 Bash 的程序员们。那么,我们开始吧,好吗?
它是一种脚本语言
重要的是要记住,Bash 是一种脚本语言,这意味着它不提供编程语言必须提供的标准功能,例如:
- 原生不支持面向对象编程
- 虽然可以使用外部应用程序(例如curl)但是没有像 Python 的请求或 Node 的axios这样的外部库
- 变量不支持类型化,所有值均按字符串计算。但是,可以使用特定命令来使用数字,例如使用-eq 测试相等性,以及使用 ((VAR_NAME+1)) 增加变量值。不过,有一种“弱”的方法来声明变量类型,即使用declared 命令。
- Bash 的关联数组(类似 Python 的dict或 JavaScript 的对象)从Bash v4.4版本开始受支持,需要注意的是,macOS 附带的是 Bash v3.2(我们将在本系列的后续博客文章中介绍这一点)
- 命名约定没有“真实来源”。例如,如何命名全局变量
Pascal_Case
????snake_case
SCREAMING_SNAKE_CASE
正如你已经猜到的,“Bash 程序员”(如果真有这种人的话)面临着许多挑战。以上列出的只是冰山一角。
以下是与我有相同感受的精彩博客文章:
既然我们已经讨论了我爱上 Bash 的事实,我想与你们分享这种感觉;开始吧。
变量命名约定
以下是我在 Bash 脚本中命名变量的方法
类型 | 范围 | 习俗 |
---|---|---|
环境 | 全球的 | 我的变量 |
全球的 | 全球的 | _我的_变量 |
当地的 | 功能 | 我的变量 |
在我之前的 Bash 脚本中,变量的名称很难理解。改用这个命名约定对我理解变量的作用域及其用途有很大帮助。
良好氛围应用
当然,我们必须看到一些实际的例子,下面是我在good_vibes.sh
应用程序中实现上述命名约定的方法。
good_vibes.sh
#!/usr/bin/env bash
# ^ This is called a Shebang
# I'll cover it in future blog posts
# Global variables are initialized by Env Vars.
# I'm setting a default value with "${VAR_NAME:-"DEFAULT_VALUE"}"
_USER_NAME="${USER_NAME:-"$USER"}"
_USER_AGE="${USER_AGE:-""}"
complement_name(){
local name="$1"
echo "Wow, ${name}, you have a beautiful name!"
}
complement_age(){
local name="$1"
local age="$2"
if [[ "$age" -gt "30" ]]; then
echo "Seriously ${name}? I thought you were $((age-7))"
else
echo "Such a weird age, are you sure it's a number?"
fi
}
main(){
# The only function that is not "pure"
# This function is tightly coupled to the script
complement_name "$_USER_NAME"
complement_age "$_USER_NAME" "$_USER_AGE"
}
# Invokes the main function
main
good_vibes.sh - Execution and output
export USER_NAME="Julia" USER_AGE="36" && \
bash good_vibes.sh
# Output
Wow, Julia, you have a beautiful name!
Seriously Julia? I thought you were 29
让我们将good_vibes.sh
应用程序分解为可以在脚本中实现的“一组规则”。
代码块间距
每个代码块之间的两 (2) 个空白行使脚本更具可读性。
缩进
我使用了两个空格,但使用四个空格缩进也是完全没问题的。只要确保不要混淆即可。
花括号
如果是${VARIABLE} concatenated with string
,请使用花括号,因为它更易于阅读。
如果是的话,那就"$LONELY_VARIABLE"
没有必要了,因为它会帮助你更快地意识到它是否“孤独”。
花括号的主要用途是执行Shell 参数扩展,如全局变量初始化部分所示。
方括号
使用双方括号可以更轻松地阅读条件代码块。但是,请注意, Shell sh[[ ]]
不支持使用双方括号;您应该使用单方括号。[ ]
为了证明可读性,这里有一个“复杂”的条件代码块:
if [[ "$USER_NAME" = "Julia" || "$USER_NAME" = "Willy" ]] \
&& [[ "$USER_AGE" -gt "30" ]]; then
echo "Easy to read right?"
fi
# Mind that `||` is replaced with `-o`, see https://acloudguru.com/blog/engineering/conditions-in-bash-scripting-if-statements
# Thank you William Pursell
if [ "$USER_NAME" = "Julia" -o "$USER_NAME" = "Willy" ] \
&& [ "$USER_AGE" -gt "30" ]; then
echo "No idea why but I feel lost with single brackets."
fi
如果你还没注意到,你刚刚学到了||
代表OR
,&&
代表AND
。而简短的-gt表达式表示greater than
使用数字时的情况。最后,该\
字符允许分行,从而使代码更具可读性。
技巧:使用
\
多余的空格\ <- extra space
可能会导致奇怪的错误。请确保 后面没有空格\
。
我认为使用[[ ]]
感觉更直观,因为大多数条件命令都是双倍的&&
||
。
变量初始化
全局变量用环境变量初始化,如果环境变量为空,则设置默认值。
正如good_vibes.sh
评论中提到的,我正在设置一个默认值
"${VAR_NAME:-"DEFAULT_VALUE"}"
上面的代码片段中,文本DEFAULT_VALUE
是硬编码的,可以用变量替换。例如
_USER_NAME="${USER_NAME:-"$USER"}"
函数和局部函数变量
函数名称和local
函数变量名称均为snake_cased
。您也可以将函数名称更改为lowerCamelCase
,当然,您可以自行决定。
将函数与脚本耦合是一个常见的错误,尽管我有时也会犯这样的错误,而且你会在我的函数中看到全局/环境变量,但当我知道“这段代码不会发生太大变化”时就会发生这种情况。
哦,确保你不要$1
直接使用或任何其他参数;总是使用local var_name="$1"
。
_USER_NAME="${USER_NAME:-"$USER"}"
# Bad - coupled
coupled_username(){
echo "_USER_NAME = ${_USER_NAME}"
}
# Good - decoupled
decoupled_username(){
local name="$1"
echo "name = ${name}"
}
# Usage
coupled_username
decoupled_username "$_USER_NAME"
函数式编程
本主题涉及函数和局部函数变量,其中函数尽可能“纯粹”。正如您在 中所见,除了初始化全局变量good_vibes.sh
之外,几乎所有内容都包装在函数中。
我不明白编写这个init_vars
处理全局变量的函数有什么意义。不过,我发现自己validate_vars
时不时会添加一个函数,用来遍历全局变量并验证它们的值。我相信这里面肯定有值得商榷的地方,所以欢迎大家发表你的看法。
最后的话
“Good Vibes 应用程序”主要介绍如何按照函数式编程范式编写可读的 Bash 脚本。
如果你觉得有必要改变变量和函数的命名方式,那就去做吧!只要你的代码容易理解,你就走在正确的道路上。
本系列的下一篇博文将涵盖以下主题:
- 错误处理
- 从 HTTP 端点检索 JSON 数据
- 使用fswatch在后台执行任务并监视文件变化
- Git 存储库结构 - 将 Bash 脚本添加到现有存储库或使用 Bash CLI 应用程序创建新存储库
- 将 Bash CLI 发布为Docker镜像
还有更多,更多……我会把所有想法都写进博文里。欢迎大家在评论区提出问题或建议,我会继续更新我的下一篇博文。
文章来源:https://dev.to/unfor19/writing-bash-scripts-like-a-pro-part-1-styling-guide-4bin