每个工程师都应知道的 10 个 Bash 脚本结构【转】

每个工程师都应知道的 10 个 Bash 脚本结构

工程师掌握 Bash:10 个结构统治一切

Bash 脚本是工程师的超能力。无论是自动化重复任务、连接工具还是管理系统,Bash 总是简单而强大。

但就像任何力量一样,它需要掌握。让我通过一个可能的场景,带您了解 10 个关键的 Bash 结构。

场景

您需要分析来自多个文件的服务器日志,提取失败的登录尝试,并生成报告。这是一个常规问题,但通过 Bash,我们将使其优雅且可重用。

1. 用脚本设置舞台

我们通过编写脚本框架开始我们的旅程:

#!/bin/bash

set -e # 遇到错误时退出
trap 'echo "错误在第 $LINENO 行"; exit 1' ERR

为什么?:

  • set -e 确保脚本在遇到第一个问题时停止。
  • trap 捕获错误,为我们提供有用的调试信息。

2. 使用函数模块化

好的脚本是模块化的。让我们定义一个函数来解析日志文件:

parse_logs() {

local file="$1"
local output="$2"

whileread -r line; do
    if [[ "$line" == *"FAILED LOGIN"* ]]; then
        echo"$line" >> "$output"
    fi

done < "$file"
}

为什么?:

  • 函数使脚本可重用且易于维护。
  • local变量防止意外覆盖。

3. 数组:管理多个日志

我们需要处理来自多个服务器的日志:

log_files=("server1.log" "server2.log" "server3.log")
results=()

for file in "${log_files[@]}"; do
    output="${file%.log}_failed.log"
    parse_logs "$file" "$output"
    results+=("$output")
done

为什么?:

  • 数组帮助高效管理项目列表。
  • 我们将处理后的结果附加到数组中以供后续步骤使用。

4. 命令替换:添加时间戳

让我们使用date 为输出文件添加时间戳:

timestamp=$(date "+%Y-%m-%d")
final_report="failed_logins_$timestamp.txt"

为什么?:

  • 命令替换无缝地将动态值集成到脚本中。

5. 字符串操作

在合并日志之前,我们清理输出文件名:

for file in "${results[@]}"; do
    sanitized_name="${file// /_}"  # 将空格替换为下划线
    mv "$file" "$sanitized_name"
done

为什么?:

  • Bash 的参数扩展简化了字符串转换,无需外部工具。

6. 进程替换:合并文件

高效合并日志:

cat "${results[@]}" > "$final_report"

为什么?:

  • 进程替换和数组扩展使处理多个文件简洁高效。

7. 条件逻辑:定制报告

根据内容定制最终报告:

if [[ -s "$final_report" ]]; then
    echo "报告已生成: $final_report"
else
    echo "未找到失败的登录。"
    rm "$final_report"
fi

为什么?:

  • if 确保操作依赖于上下文,例如报告是否为空。

8. 案例语句:默认端口

假设我们需要根据服务器类型识别默认的 SSH 和 HTTPS 端口:

get_port() {
    local server="$1"
    case "$server" in
        "prod"*) echo 22 ;;
        "staging"*) echo 2222 ;;
        *) echo 80 ;;
    esac
}

为什么?:

  • case 优雅地处理多个特定模式。

9. 使用set -x 调试

在部署脚本之前,让我们调试它:

set -x # 启用调试
# 在此运行主脚本
set +x # 禁用调试

为什么?:

  • 调试工具如set -x 使跟踪和修复错误变得容易。

10. 文件描述符用于高级 I/O

假设我们从特殊输入流读取和处理日志:

exec 3<"$final_report"

while read -u3 line; do
    echo "已处理: $line"
done

exec 3<&-

为什么?:

  • 文件描述符提供对输入输出的精确控制,支持并行处理。

最终脚本

以下是打磨后的脚本可能的样子:

#!/bin/bash

set -e
trap'echo "错误在第 $LINENO 行"; exit 1' ERR
parse_logs() {
    local file="$1"
    local output="$2"
    whileread -r line; do
        if [[ "$line" == *"FAILED LOGIN"* ]]; then
            echo"$line" >> "$output"
        fi
    done < "$file"
}
log_files=("server1.log""server2.log""server3.log")
results=()
for file in"${log_files[@]}"; do
    output="${file%.log}_failed.log"
    parse_logs "$file""$output"
    results+=("$output")
done
timestamp=$(date "+%Y-%m-%d")
final_report="failed_logins_$timestamp.txt"
cat "${results[@]}" > "$final_report"
if [[ -s "$final_report" ]]; then
    echo"报告已生成: $final_report"
else
    echo"未找到失败的登录。"
    rm "$final_report"
fi

收获

这个脚本结合了工程师在专业 Bash 脚本中几乎需要的所有内容:模块化、错误处理、高效数据处理和调试工具。

通过掌握这些结构,您不仅会编写更好的脚本,还会将平凡的任务转化为优雅的解决方案。

但是,还有一件事(也许是 5 件)。我现在太兴奋了,我会再包含 5 个我经常使用的额外内容:

每个工程师都应知道的 5 个额外 Bash 结构

以下是五个额外的结构:

1. 关联数组

它们是什么: 关联数组是 Bash 中的键值对,从 Bash 4 开始可用。它们允许高效查找和数据组织。

示例: 假设您正在将服务器名称映射到其 IP 地址:

declare -A servers

servers=( ["web"]="192.168.1.10" ["db"]="192.168.1.20" ["cache"]="192.168.1.30" )

# 访问值
echo "Web 服务器 IP: ${servers[web]}"
# 遍历键
for key in "${!servers[@]}"; do
    echo "$key -> ${servers[$key]}"
done

为什么使用它们:

  • 关联数组提供了一种自然的方式来处理结构化数据,无需依赖外部工具如awk 或sed
  • 适用于配置、查找和在脚本中动态组织数据。

2. 使用 Heredocs 进行多行输入

它们是什么: Heredocs 允许在脚本中直接输入多行字符串或数据,提高处理模板或批量数据时的可读性。

示例: 动态生成电子邮件模板:

email_body=$(cat <<EOF
Hello Team,

This is a reminder for the upcoming deployment at midnight.

Regards,
DevOps
EOF)

echo "$email_body" | mail -s "Deployment Reminder" team@example.com

为什么使用它们:

  • 它们消除了复杂字符串连接或外部文件的需求。
  • Heredocs 简化了直接在脚本中处理多行内容,如日志、模板或命令。

3. 使用eval 进行动态命令执行

它是什么:eval 命令允许您将动态构造的字符串作为 Bash 命令执行。

示例: 假设您需要执行存储在变量中的命令:

cmd="ls -l"
eval "$cmd"

或动态设置变量:

var_name="greeting"
eval "$var_name='Hello, World!'"
echo "$greeting"

为什么使用它:

  • eval 提供了处理动态生成命令或输入的灵活性。
  • ⚠ 谨慎使用:虽然强大,但不当使用eval 可能会带来安全风险,尤其是在处理不受信任的输入时。

4. 子 shell 用于隔离执行

它们是什么: 子 shell 是执行命令而不影响父 shell 的子进程。

示例: 假设您想临时更改目录并执行命令:

(current_dir=$(pwd)
cd /tmp
echo "Now in $(pwd)"
)
echo "Back in $current_dir"

为什么使用它们:

  • 子 shell 允许临时更改变量、环境或目录,而不会影响主 shell。
  • 适用于运行不会污染或修改父环境的隔离操作。

5. 命名管道(FIFOs)

它们是什么: 命名管道(或 FIFOs)是特殊文件,通过充当命令之间的缓冲区来促进进程间通信。

示例: 让我们创建一个命名管道在进程之间传输数据:

mkfifo my_pipe

# 在一个终端:写入管道
echo "Hello from process 1" > my_pipe

# 在另一个终端:从管道读取
cat < my_pipe

# 清理
rm my_pipe

为什么使用它们:

  • 命名管道支持进程之间的异步通信,无需临时文件。
  • 适用于实时处理场景,如在命令之间传输日志或流数据。

结论

这些额外的结构——关联数组、Heredocs、eval、子 shell 和命名管道——扩展了您的 Bash 脚本工具包,以应对更复杂的任务。

通过掌握这些结构,您将编写更优雅、高效且易于维护的脚本,以应对实际的工程挑战。

祝您编写愉快!🖥️

转自

每个工程师都应知道的 10 个 Bash 脚本结构
https://mp.weixin.qq.com/s/wdKifwG-cfPc1LAkZiSgBw

 

posted @   paul_hch  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
历史上的今天:
2022-01-06 Unison(双向同步软件)的安装与配置【转】
2022-01-06 Nginx proxy_pass使用$host变量【转】
2022-01-06 Nginx URL重写rewrite配置及信息详解【转】
2022-01-06 Nginx之https配置 - 运维笔记【转】
2022-01-06 linux系统内核版本升级【转】
2018-01-06 MySql 死锁时的一种解决办法【转】
点击右上角即可分享
微信分享提示