百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

Bash脚本编写技巧和窍门(bash脚本调试)

toqiye 2024-10-01 22:55 1 浏览 0 评论

作为一名DevOps工程师,我经常会遇到Bash脚本。在服务器上执行任务、编写CI/CD流水线或者自动化一些手动任务,这些都是日常活动,通常都需要编写一些Bash脚本。在这里,我将分享一些在Bash脚本编写时应该遵循的技巧和最佳实践。

1. 区分变量

在Bash中,对于变量有两个众所周知的良好实践:

  1. 总是在变量名周围使用大括号 {}
  2. 总是在变量周围使用双引号 ""

因此,一个变量应该像这样:"${variable_name}"。这样可以清晰地区分变量与代码的其他部分,并且变量值中的空格不会导致问题。让我们通过一个示例来说明有关空格的问题。

mkdir tips
echo "File content" > "tips/Filename with spaces"
ls -l tips

运行上述命令后,我们有一个包含一个文件的 tips 目录,但是该文件名中有空格。现在让我们尝试遍历 tips 目录中的文件。应该只有一个文件。

for filename in $(ls tips); do
  echo Found: ${filename}
  cat tips/${filename}
done

上述 for 循环将生成:

Found: Filename
cat: tips/Filename: No such file or directory
Found: with
cat: tips/with: No such file or directory
Found: spaces
cat: tips/spaces: No such file or directory

显然,输出结果与我们预期的不符。现在尝试相同的 for 循环,但是添加双引号。

for filename in "$(ls tips)"; do
  echo Found: "${filename}"
  cat tips/"${filename}"
done

注意,双引号也应该放在命令输出周围,即此处为 "$(ls tips)"。最终输出现在应该是正确的:

Found: Filename with spaces
File content

在这个示例中,echocat 也可以这样写:

  echo "Found: ${filename}"
  cat "tips/${filename}"

关于变量还有一件事需要注意。局部变量通常使用小写字母编写,环境变量则使用大写字母编写。运行 env 命令以查看当前终端的环境变量 - 它们都是大写的。因此,在使用 export 将变量从脚本导出到环境时,请使用大写字母。

export cluster_name="r2d2" # 不好
export CLUSTER_NAME="r2d2" # 好

2. 处理错误

不幸的是,Bash没有直观、简单的错误处理机制。无法使用 try-catch 块捕获异常。检查错误的唯一方法是评估命令的退出代码。无论您是交互式运行脚本还是非交互式运行脚本,脚本都应该检查这些错误代码。最简单的方法是使用 if 并检查 $? 中的退出代码:

if cat file; then
  echo "退出代码为 0。没有问题。"
else
  echo "退出代码为 $?"
  echo "处理错误.."
fi

如果您没有使用 if 检查,则将 set -e 添加到脚本中。使用此选项,Bash 脚本将在某个命令失败时退出。这在其他编程语言中是预期的行为,但默认情况下,Bash 如果脚本中的命令失败,它并不会退出。

set -e
cat no_file # 抛出错误
cat no_file2 # 此部分不会被执行

使用 set -e,只有 cat no_file 会被执行,并且脚本将退出。现在尝试移除 set -e。您会看到在这种情况下都会运行两个 cat 命令,即使它们都失败了。

您还应该添加 set -o pipefail,这样如果管道中的某个命令失败,整个管道就会失败。默认情况下,管道只有在最后一个命令失败时才会失败。

set -eo pipefail
cat no_file | echo # 抛出错误
echo "End" # 此部分不会被执行

此外,还有 set -u,它将未定义(未绑定)变量的使用视为错误。

set -euo pipefail
echo "${var}" # 抛出错误并退出

如果您移除 -u 选项,脚本将成功退出,而 echo 将打印空字符串。

3. 使用缩进和多行

在Bash中,您可以使用 ; 在一行文本中容纳整个脚本。显然,没有人会这样做,但很多人会在单行中写入很长的命令和很多参数。这不是一个好的实践,因为您无法轻松地看到整个命令。相反,将该行拆分为多行,并添加缩进。例如:

az aks create \
  --resource-group my-resource-group \
  --name my-aks-cluster \
  --enable-managed-identity \
  --node-count 1 \
  --enable-addons monitoring \
  --generate-ssh-keys

有些人可能会说您可以将两个或三个参数放在一行中,但我更喜欢每个参数单独放在一行中,因为这更清晰。有趣的是,我从Microsoft Azure文档中提取了上述命令,他们将所有内容放在单行中,因此您必须水平滚动以查看要复制的命令:

https://learn.microsoft.com/en-us/azure/aks/learn/quick-kubernetes-deploy-cli#create-an-aks-cluster)

另一方面,Google Cloud文档确实将命令拆分为多行,因此更容易阅读:

此外,我强烈建议在编写if-else块和循环时使用缩进。以下是一些示例:

if [[ "${PASSWORD}" == "${ADMIN_PASSWORD}" ]]; then
  echo "Access granted"
else
  echo "Access denied"
fi
while "${RUN}"; do
  if [[ -f new_file ]]; then
    break
  fi
  sleep 1
done

此外,请确保规范化缩进应为两个空格还是四个空格。我更喜欢两个空格,但您或您的团队可能更喜欢四个。只需确保保持一致即可。

4. 使用 Shellcheck 进行测试

Shellcheck 是一个用于 Bash 的代码检查工具。您可以使用 apt、dnf、pkg 和 brew 进行安装,使用 在线版本,或者直接将其安装到您的 编辑器 中。

sudo apt install shellcheck

要检查一个脚本,只需执行:

shellcheck script.sh

Shellcheck 会方便地提醒您添加一个 shebang (#!/bin/bash),添加双引号,如果您错误使用引号或使用未赋值的变量,它还会警告您,告诉您条件表达式中的问题,以及更多。这真的非常有用,特别是如果您对 Bash 还不是很熟悉的话。我还建议将 Shellcheck 添加到您的 CI/CD 流水线中。

5. 在必要的地方添加注释

注释可以帮助其他人阅读您的脚本。当您阅读自己的旧脚本时,注释也可以帮助您,因为您可能已经忘记了该脚本,但现在又需要它。您不必花费时间编写深入的注释,但一定要在那些有点难以理解的行上添加注释。随着时间的推移,您会养成自动编写这些注释的习惯,甚至无需考虑。

# 获取所有启用了 prometheus 抓取的部署
kubectl get deployments \
  --all-namespaces \
  -o=jsonpath='{.items[?(@.spec.template.metadata.annotations.prometheus\.io/scrape=="true")].metadata.name}'

这是我在 Stack Overflow 上找到的一个示例命令,如果您要在脚本中使用它,那么绝对需要添加注释。

6. 使用超时设置

我遇到过许多情况,脚本必须等待某些外部任务完成。例如,它必须等到文件创建完成,等到 Kubernetes Pod 运行完成,等到虚拟机启动完成,等到某个作业完成等等。在这种情况下,您应该为该命令或循环定义一个超时,因为云中(或任何外部服务中)可能会出现故障,资源可能永远无法启动。要为命令添加超时,您可以简单地使用 timeout 命令。如果超时,退出状态为 124。尝试运行下面的示例,并缩短睡眠时间,以便打印出 Done

timeout 2s sleep 5 && echo Done

对于循环,您可以使用以下技巧:

# 等待 new_file 10 秒钟
timeout 10s bash -c \
  'until [[ -f new_file ]]; do
    echo "User ${USER} is waiting a file.."
    sleep 1
  done'

在单引号内部使用双引号将起作用,因为 bash -c 启动一个新的 shell,然后单引号之间的所有内容都会像解析您的 shell 输入一样解析。尝试运行循环,查看 timeout 在 10 秒后将其终止的情况。再次运行循环,并在同一目录中快速创建 new_file。循环应该在没有超时的情况下成功退出。

7. 找到合适的工具

在编写脚本时,了解有很多工具可以帮助您是很重要的。我记得有一次实习生的任务是使用 Bash 脚本解析 YAML。他必须评估目录中的所有文件。如果文件具有特定的 YAML 键,并且该键的值为数组,他必须评估数组的所有元素。为了简化示例,让我们假设他必须评估每个元素是否包含字符 r

yamlKey:
  - first
  - second
  - third
anotherKey: "val"

因此,他使用 grep -n 查找文件是否包含 yamlKey 并保存键的行号。然后,他使用 sed 读取下一行,并检查它是否以两个空格和一个减号开头——这意味着它是数组的一个元素。在这种情况下,他将使用正则表达式检查该行是否包含 r。然后,他将继续下一行。如果该行不以两个空格和一个减号开头,则表示数组已结束。阅读代码是痛苦的。代码实际上是有效的,但仅在一段时间内有效。在 YAML 中,还有另一种定义数组的方式:

yamlKey: [first, second, third]
anotherKey: "val"

这就是为什么在编写一些 hacky 解决方案之前,您应该先稍微搜索一下。很可能有一个程序可以解决您的问题。在这种情况下,就是 yq,一个用于处理 YAML 语法的命令行界面。

8. 考虑对于复杂任务使用 Python

关于 Python 和 Bash 哪个更适合哪种情况的辩论永远不会停止。Python 更强大,但 Bash 更通用和轻量级。Bash 基本上可以在每台 Linux 服务器上和每个 CI/CD 工具上默认使用。了解 Bash 肯定是有用的。但是当涉及到发送大量 GET 和 POST 请求、与 API 进行交互和处理大量数据等复杂任务时,Python 更容易编写。在 Bash 中,您将不得不进行大量的文本解析和错误检查。尽管如此,Bash 仍然是 DevOps 工程师中的一种流行选择。大多数 DevOps 教程和许多文档主要使用命令行界面来解释如何执行某些任务。例如,Kubernetes 有一个 Python 客户端,但是当搜索如何在 Kubernetes 中执行某项任务时,您总是会找到可以在 Bash 中运行的 kubectl 命令。

相关推荐

完美解决MAC电脑空间不足问题(完美解决mac电脑空间不足问题的办法)

很多用MAC(苹果笔记本)电脑的人,特别是做iOS开发的,都会遇到一个头疼的问题,那就是电脑磁盘空间不足的问题。这个问题也困扰了我好久,我的开发机是256G的SSD(固态硬盘),但是用着用着就会空间不...

系统清理软件Omni Remover for Mac版

内容介绍你是否需要一款可以帮你清理Mac系统顽固垃圾的工具呢?试试OmniRemoverforMac吧!OmniRemoverMac版是一款运行在Mac平台上的系统清理软件。OmniRem...

mac上一款好用的多功能系统清理软件Omni Remover for Mac

mac上一款好用的多功能系统清理软件——OmniRemoverforMac。OmniRemovermac破解版是Mac平台上的一款软件清理工具。OmniRemoverMac版专为优化内存...

清理重建mac OS图标缓存(mac系统清空)

关于macos缓存问题你了解多少?今天macdown小编带大家了解下有关Mac清除图标缓存的相关知识!你知道吗?为了提升图形界面加载速度,默认情况下macOS针对Finder和Dock中的...

iOS 9 人机界面指南(五):图标与图形设计

来人人都是产品经理【起点学院】,BAT实战派产品总监手把手系统带你学产品、学运营。文章索引5.1图标与图像尺寸(IconandImageSizes)5.2应用图标(AppIcon)5.2....

你中招了吗?盘古团队发布XcodeGhost病毒检测应用

最近大批知名iOS应用被感染XcodeGhost病毒事件闹得沸沸扬扬,虽然该病毒作者发表声明称,XcodeGhost源于自己的实验,没有任何威胁性行为,同时公开了源代码。但依然无法消除众多用户的担忧,...

iOS应用感染Xcode真是无恶意实验?感染APP最新名单及版本号

前瞻科技快讯9月19日消息,一向号称是最安全的iOS真的不安全了?对于这两天闹得沸沸扬扬的多款iOS应用感染XcodeGhost病毒事件,今日凌晨4点左右,网友@XcodeGhost-Author在微...

苹果应用签名失败怎么处理(ios应用签名什么意思)

在移动应用开发过程中,苹果应用签名失败是一种常见的问题,它可能由多种原因引起。本文将介绍一些处理苹果应用签名失败的方法,帮助开发者解决这个问题。检查证书和描述文件:首先,开发者应该检查使用的证书和描述...

好用的系统扫描和清理工具推荐:OS Cleaner Pro for Mac

为大家推荐一款全面的系统扫描和清理工具,OSCleanerProforMac...

系统清理软件 Omni Remover for Mac

你是否需要一款可以帮你清理Mac系统顽固垃圾的工具呢?试试OmniRemoverforMac吧!OmniRemoverMac版是一款运行在Mac平台上的系统清理软件。OmniRemover...

优秀的Mac系统清理软件(mac清理系统占用空间)

OmniRemoverforMac是一款优秀的系统清理软件,功能有清洁卸载膨胀且顽固的应用程序,在macOSCatalina上清除32位不兼容的应用程序,iTunes,Xcode和Sketc...

苹果app安卓apk应用内用微信登录游戏时会显示登录失败怎么解决?

解决苹果iOS应用和安卓APK应用在使用微信授权登录时出现“登录失败,签名不一致”的问题,可以按照以下步骤进行排查和解决:1.核实AppID和AppSecret:确保iOS和安卓项目中使用的微信开放...

Cleaner for Xcode(遗留废弃文件清理工具)

Mac上的Xcode总是占用很大空间,并且用的时间越久越大!可通过删除不需要的和不建议使用的文件来帮助您加快Xcode的运行速度,你可以每月或者每周运行一次进行清理。有需要的朋友,赶快来下载吧~Cle...

Cleaner for Xcode mac(xcode清理工具)

Xcode文件太多,如何检测清理?试试CleanerforⅩcode吧!CleanerforXcodeforMac可以检测您的Xcode占用磁盘的情况,统计各个部件所占用的空间。并帮助您...

柠檬清理一款Mac设备必备的实用工具

简介柠檬清理是针对macOS系统专属制定的清理工具。主要功能包括重复文件和相似照片的识别、软件的定制化垃圾扫描、可视化的全盘空间分析、内存释放、浏览器隐私清理以及设备实时状态的监控等。重点聚焦清理功能...

取消回复欢迎 发表评论: