兰 亭 墨 苑
期货 · 量化 · AI · 终身学习
首页
归档
编辑文章
标题 *
URL 别名 *
内容 *
(支持 Markdown 格式)
# Bash 脚本功能与原理深度解析:一言微信推送器 我们来详细解释这个 Bash 脚本的功能、工作原理以及其中涉及的技术细节。这个脚本的目的是获取一句随机的“一言”语录,然后通过一个微信推送接口将其发送出去。 --- ## 脚本概览 这个 Bash 脚本是一个自动化工具,它执行两个主要任务: 1. **获取随机“一言”语录:** 通过调用 `https://v1.hitokoto.cn/` 这个公共 API,获取一句随机的短句和其来源。 2. **发送到微信:** 利用 `https://api.yuangs.cc/weixinpush` 这个微信推送接口(看起来是一个第三方服务,可能需要配置,或者是一个测试接口),将获取到的“一言”内容发送到指定的微信接收者。 整个过程是自动化的,无需人工干预。 ## 一、脚本头部:Shebang 与变量定义 ```bash #!/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.` 开头,后面跟着随机字符的文件名,并确保这个文件名在文件系统中的唯一性。这样做是为了避免多个脚本同时运行时文件命名冲突,也方便后续清理。脚本会在运行结束时删除这个临时文件。 ## 二、获取一言数据 ```bash # 获取一言数据 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`,失败时返回非 `0`。`if ! command` 表示如果 `command` 执行失败(退出状态码非 `0`)则执行 `then` 块内的代码。 * **错误处理:** 如果 `curl` 命令因网络问题(如 DNS 解析失败、连接超时)而无法成功获取数据,`then` 块内的代码将被执行。 * `echo "❌ 获取一言失败:网络错误"`: 输出错误信息。 * `rm -f "$TEMP_JSON"`: `rm` 命令用于删除文件。`-f` (force) 选项表示强制删除,不提示确认。即使文件不存在也不会报错。这确保了如果 `curl` 失败导致文件未完全生成或损坏,也能清理掉。 * `exit 1`: `exit` 命令用于**终止脚本的执行**。参数 `1` 表示脚本以非零状态码退出,通常表示执行失败。 ## 三、检查并提取一言内容 ```bash # 检查返回的数据是否有效 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 处理器**。它允许你像使用 `sed` 或 `awk` 处理文本一样处理 JSON 数据,进行查询、过滤和转换。 * `jq -e .hitokoto "$TEMP_JSON"`: * `-e`: `exit code` 选项。如果 `jq` 的表达式(这里是 `.hitokoto`)的结果为 `false` 或 `null`,或者输入不是有效的 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_who` 和 `from` 字段,并将它们拼接成 `from_who《from》` 的格式。 * `2>/dev/null`: 这里的 `2>/dev/null` 用于丢弃 `jq` 在 `from_who` 或 `from` 字段不存在时可能输出的错误信息。 * **来源格式化逻辑:** * `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` 且非空)。如果有效,则将 `CONTENT` 和 `FROM` 拼接成 `内容\n—— 来源` 的格式赋值给 `FULL_CONTENT`;否则,`FULL_CONTENT` 只有 `CONTENT`。 * **显示完整内容:** * `echo "📖 今日一言:"`: 输出一个带有表情符号的标题。 * `echo "$FULL_CONTENT"`: 输出最终构造好的一言内容。 ## 四、发送到微信接口 ```bash # 发送到微信接口 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-Type` 为 `application/json`,告知服务器请求体是 JSON 格式。 * `-d "$POST_DATA"`: `data` 选项。将 `POST_DATA` 变量的内容作为 HTTP POST 请求体发送。 * `"$WECHAT_API"`: 微信推送接口的 URL。 * `>/dev/null`: 将 `curl` 的标准输出重定向到 `/dev/null`,不显示服务器响应。 * **错误处理:** 如果 `curl` 命令成功(退出状态码为 `0`),则输出“成功发送到微信”;否则输出“发送到微信失败”。 ## 五、清理临时文件 ```bash # 清理临时文件 rm -f "$TEMP_JSON" ``` * `rm -f "$TEMP_JSON"`: 最后,脚本会删除在开头使用 `mktemp` 创建的临时文件,确保不留下垃圾文件。这是良好脚本编写习惯的一部分。 ## 总结 这个 Bash 脚本是一个实用且结构清晰的自动化流程: 1. **数据获取:** 利用 `curl` 和 `jq` 从外部 API 获取并结构化数据。 2. **数据处理:** Bash 变量和条件判断用于处理、格式化获取到的数据。 3. **数据发送:** 再次利用 `curl` 和 `jq` 构造并发送 POST 请求,将数据推送给另一个外部服务。 4. **健壮性:** 包含了基本的错误检查和清理机制。 这个脚本很好地展示了如何组合使用常见的 Linux/UNIX 命令行工具 (`curl`, `jq`, `mktemp`, `rm`, `echo`) 来实现自动化任务,同时体现了 Bash 脚本编写中错误处理和临时文件管理的最佳实践。
配图 (可多选)
选择新图片文件或拖拽到此处
标签
更新文章
删除文章