Bash 脚本功能与原理深度解析:一言微信推送器

Bash 脚本功能与原理深度解析:一言微信推送器

我们来详细解释这个 Bash 脚本的功能、工作原理以及其中涉及的技术细节。这个脚本的目的是获取一句随机的“一言”语录,然后通过一个微信推送接口将其发送出去。


脚本概览

这个 Bash 脚本是一个自动化工具,它执行两个主要任务:

  1. 获取随机“一言”语录: 通过调用 https://v1.hitokoto.cn/ 这个公共 API,获取一句随机的短句和其来源。
  2. 发送到微信: 利用 https://api.yuangs.cc/weixinpush 这个微信推送接口(看起来是一个第三方服务,可能需要配置,或者是一个测试接口),将获取到的“一言”内容发送到指定的微信接收者。

整个过程是自动化的,无需人工干预。

一、脚本头部:Shebang 与变量定义

#!/bin/bash  
  
# 一言获取并发送到微信的脚本  
  
# 设置API地址  
HITOKOTO_API="https://v1.hitokoto.cn/?c=a&c=b&c=c&c=d&c=e&c=f&c=g&c=h&c=i&c=j&c=k"  
WECHAT_API="https://api.yuangs.cc/weixinpush"  
  
# 临时文件存储JSON响应  
TEMP_JSON=$(mktemp)  
  • #!/bin/bash (Shebang):

    • 这是脚本的第一行,被称为 Shebang(或 Hashbang)。
    • 它告诉操作系统应该使用哪个解释器来执行这个脚本文件。在这里,它指定使用 /bin/bash 这个 Bash shell 程序来解释并运行脚本的其余部分。
  • 注释 (#):

    • # 开头的行是注释,用于解释脚本的功能、目的或某段代码的含义,对脚本的执行没有影响。
  • API 地址变量定义:

    • HITOKOTO_API="https://v1.hitokoto.cn/?c=a&c=b&c=c&c=d&c=e&c=f&c=g&c=h&c=i&c=j&c=k": 定义了一个变量 HITOKOTO_API,存储一言 API 的 URL。URL 中的 ?c=a&c=b... 部分是 API 参数,用于指定语录的分类,这里选择了多个分类。
    • WECHAT_API="https://api.yuangs.cc/weixinpush": 定义了一个变量 WECHAT_API,存储微信推送接口的 URL。这个接口是一个第三方服务,其具体使用可能需要注册、API Key 或其他配置,脚本中并未体现认证信息。
  • 临时文件存储JSON响应:

    • TEMP_JSON=$(mktemp): 定义一个变量 TEMP_JSON,用于存储从一言 API 获取到的 JSON 响应。
    • mktemp: 这是一个 Bash 命令,用于创建一个唯一的临时文件或目录的名称。它会生成一个以 tmp. 开头,后面跟着随机字符的文件名,并确保这个文件名在文件系统中的唯一性。这样做是为了避免多个脚本同时运行时文件命名冲突,也方便后续清理。脚本会在运行结束时删除这个临时文件。

二、获取一言数据

# 获取一言数据  
echo "正在获取一言..."  
if ! curl -s -o "$TEMP_JSON" "$HITOKOTO_API"; then  
    echo "❌ 获取一言失败:网络错误"  
    rm -f "$TEMP_JSON"  
    exit 1  
fi  
  • echo "正在获取一言...":

    • echo 命令用于在终端输出文本。这行代码在脚本执行时,会向用户显示当前正在进行的操作。
  • if ! curl -s -o "$TEMP_JSON" "$HITOKOTO_API"; then ... fi (条件判断与 curl 命令):

    • curl: 这是一个强大的命令行工具,用于传输数据,支持多种协议(HTTP, HTTPS, FTP 等)。
      • -s: silent 模式。 suppresses output of progress meter or error messages. This means curl will not show download progress, nor will it show error messages like "connection refused". Only the final data will be written to the output. 这可以使脚本的输出更简洁,只关注我们想要的数据或错误信息。
      • -o "$TEMP_JSON": output 选项。将 curl 获取到的内容写入到指定的文件$TEMP_JSON 中,而不是直接输出到标准输出。
      • "$HITOKOTO_API": curl 请求的目标 URL,引用前面定义的变量。
    • !: Bash 中的逻辑非运算符。curl 命令在执行成功时会返回退出状态码 0,失败时返回非 0if ! command 表示如果 command 执行失败(退出状态码非 0)则执行 then 块内的代码。
    • 错误处理: 如果 curl 命令因网络问题(如 DNS 解析失败、连接超时)而无法成功获取数据,then 块内的代码将被执行。
      • echo "❌ 获取一言失败:网络错误": 输出错误信息。
      • rm -f "$TEMP_JSON": rm 命令用于删除文件。-f (force) 选项表示强制删除,不提示确认。即使文件不存在也不会报错。这确保了如果 curl 失败导致文件未完全生成或损坏,也能清理掉。
      • exit 1: exit 命令用于终止脚本的执行。参数 1 表示脚本以非零状态码退出,通常表示执行失败。

三、检查并提取一言内容

# 检查返回的数据是否有效  
if ! jq -e .hitokoto "$TEMP_JSON" >/dev/null 2>&1; then  
    echo "❌ 获取一言失败:数据格式错误"  
    rm -f "$TEMP_JSON"  
    exit 1  
fi  
  
# 提取一言内容和来源  
CONTENT=$(jq -r .hitokoto "$TEMP_JSON")  
FROM=$(jq -r '.from_who + "《" + .from + "》"' "$TEMP_JSON" 2>/dev/null)  
  
# 如果没有from_who,则只使用from  
if [[ "$FROM" == "null《"*.from*"》" ]] || [[ "$FROM" == "《"*.from*"》" ]]; then  
    FROM=$(jq -r .from "$TEMP_JSON")  
fi  
  
# 构造完整内容(包含来源)  
if [[ "$FROM" != "null" ]] && [[ -n "$FROM" ]]; then  
    FULL_CONTENT="$CONTENT  
  
—— $FROM"  
else  
    FULL_CONTENT="$CONTENT"  
fi  
  
echo "📖 今日一言:"  
echo "$FULL_CONTENT"  
  • jq 命令:

    • jq 是一个轻量级且灵活的命令行 JSON 处理器。它允许你像使用 sedawk 处理文本一样处理 JSON 数据,进行查询、过滤和转换。
    • jq -e .hitokoto "$TEMP_JSON":
      • -e: exit code 选项。如果 jq 的表达式(这里是 .hitokoto)的结果为 falsenull,或者输入不是有效的 JSON,jq 就会以非零退出状态码退出。这对于在 if 语句中检查 JSON 数据有效性非常有用。
      • .hitokoto: jq 表达式,表示从 JSON 根对象中提取 hitokoto 字段的值。
      • >/dev/null 2>&1: 这是 Bash 的重定向操作符
        • >/dev/null: 将标准输出(stdout)重定向到 /dev/null/dev/null 是一个特殊的设备文件,所有写入它的数据都会被丢弃。这意味着 jq 提取到的值不会显示在终端。
        • 2>&1: 将标准错误(stderr,文件描述符为 2)重定向到标准输出(文件描述符为 1)所指向的位置。由于标准输出已经重定向到 /dev/null,所以标准错误也会被丢弃。
        • 目的: 这整个部分的作用是静默地检查 TEMP_JSON 文件是否是有效的 JSON 且包含 hitokoto 字段。如果不是,jq -e 会以失败状态码退出,从而触发 if 语句的 then 块。
  • 错误处理: 如果 JSON 数据无效或缺少 hitokoto 字段,脚本会输出错误信息,清理临时文件,并退出。

  • 内容提取与变量赋值:

    • CONTENT=$(jq -r .hitokoto "$TEMP_JSON"):
      • -r: raw 选项。输出提取到的 JSON 值为纯文本字符串,不带 JSON 字符串的引号。
      • 这行代码提取 hitokoto 字段的值并赋值给 CONTENT 变量。
    • FROM=$(jq -r '.from_who + "《" + .from + "》"' "$TEMP_JSON" 2>/dev/null):
      • 尝试提取 from_whofrom 字段,并将它们拼接成 from_who《from》 的格式。
      • 2>/dev/null: 这里的 2>/dev/null 用于丢弃 jqfrom_whofrom 字段不存在时可能输出的错误信息。
  • 来源格式化逻辑:

    • if [[ "$FROM" == "null《"*.from*"》" ]] || [[ "$FROM" == "《"*.from*"》" ]]; then ... fi:
      • 这是一个 Bash 的条件判断,检查 FROM 变量是否包含了因为字段缺失而产生的 null 字符串或不完整的格式(例如 null《xxx》《xxx》)。
      • [[ ... ]]: Bash 中用于高级条件测试的语法。
      • ==: 字符串相等比较。
      • *.from*: Bash 的模式匹配,* 代表任意字符序列。
      • ||: 逻辑或。
      • 目的: 如果 from_who 字段不存在,那么 FROM 变量就会变成 null《...》 这种形式。这个 if 语句的作用是修正这种情况,如果 from_who 缺失,则只提取 from 字段作为来源。
    • FROM=$(jq -r .from "$TEMP_JSON"): 重新提取纯粹的 from 字段。
  • 完整内容构造:

    • if [[ "$FROM" != "null" ]] && [[ -n "$FROM" ]]; then ... else ... fi:
      • &&: 逻辑与。
      • -n "$FROM": 检查变量 $FROM 是否为非空字符串。
      • 目的: 检查修正后的 FROM 变量是否有效(非 null 且非空)。如果有效,则将 CONTENTFROM 拼接成 内容\n—— 来源 的格式赋值给 FULL_CONTENT;否则,FULL_CONTENT 只有 CONTENT
  • 显示完整内容:

    • echo "📖 今日一言:": 输出一个带有表情符号的标题。
    • echo "$FULL_CONTENT": 输出最终构造好的一言内容。

四、发送到微信接口

# 发送到微信接口  
echo "正在发送到微信..."  
  
# 构造POST数据  
POST_DATA=$(jq -n \  
    --arg title "一言" \  
    --arg content "$FULL_CONTENT" \  
    --arg to_user "@all" \  
    '{  
        msgtype: "text",  
        title: $title,  
        content: $content,  
        to_user: $to_user  
    }')  
  
# 发送请求  
if curl -s -X POST \  
    -H "Content-Type: application/json" \  
    -d "$POST_DATA" \  
    "$WECHAT_API" >/dev/null; then  
    echo "✅ 成功发送到微信"  
else  
    echo "❌ 发送到微信失败"  
fi  
  • echo "正在发送到微信...": 输出提示信息。

  • POST_DATA=$(jq -n --arg title "一言" ... '{ ... }') (构造 POST 数据):

    • jq -n: -n (null input) 选项表示 jq 不从标准输入读取任何内容,而是从一个空 JSON 对象开始构造。
    • --arg name value: 命令行参数,用于将 Bash 变量的值作为 jq 表达式中的参数引入。
      • --arg title "一言": 将字符串 "一言" 赋值给 jq 内部的 $title 变量。
      • --arg content "$FULL_CONTENT": 将 Bash 变量 FULL_CONTENT 的值赋值给 jq 内部的 $content 变量。
      • --arg to_user "@all": 将字符串 "@all" 赋值给 jq 内部的 $to_user 变量。
        • 注意: @all 可能是该微信推送接口约定的一种广播给所有关注者的语法,或特定群组的 ID。
    • '{ msgtype: "text", title: $title, content: $content, to_user: $to_user }': 这是 jq 的 JSON 构造表达式。它会创建一个 JSON 对象,其中的 title, content, to_user 的值会引用前面通过 --arg 传入的 jq 内部变量。
    • 目的: 这部分代码使用 jq 精确地构造了一个符合微信推送接口要求的 JSON 格式的 POST 请求体。
  • if curl -s -X POST ... "$WECHAT_API" >/dev/null; then ... else ... fi (发送请求):

    • curl -s: 静默模式,不显示进度和错误信息。
    • -X POST: 指定 HTTP 请求方法为 POST。
    • -H "Content-Type: application/json": 设置 HTTP 请求头 Content-Typeapplication/json,告知服务器请求体是 JSON 格式。
    • -d "$POST_DATA": data 选项。将 POST_DATA 变量的内容作为 HTTP POST 请求体发送。
    • "$WECHAT_API": 微信推送接口的 URL。
    • >/dev/null: 将 curl 的标准输出重定向到 /dev/null,不显示服务器响应。
    • 错误处理: 如果 curl 命令成功(退出状态码为 0),则输出“成功发送到微信”;否则输出“发送到微信失败”。

五、清理临时文件

# 清理临时文件  
rm -f "$TEMP_JSON"  
  • rm -f "$TEMP_JSON": 最后,脚本会删除在开头使用 mktemp 创建的临时文件,确保不留下垃圾文件。这是良好脚本编写习惯的一部分。

总结

这个 Bash 脚本是一个实用且结构清晰的自动化流程:

  1. 数据获取: 利用 curljq 从外部 API 获取并结构化数据。
  2. 数据处理: Bash 变量和条件判断用于处理、格式化获取到的数据。
  3. 数据发送: 再次利用 curljq 构造并发送 POST 请求,将数据推送给另一个外部服务。
  4. 健壮性: 包含了基本的错误检查和清理机制。

这个脚本很好地展示了如何组合使用常见的 Linux/UNIX 命令行工具 (curl, jq, mktemp, rm, echo) 来实现自动化任务,同时体现了 Bash 脚本编写中错误处理和临时文件管理的最佳实践。