神觉晓

@shenjuexiao

神觉晓

POSIX 习惯

“POSIX 习惯”通常指在编写 Unix/Linux 命令行程序、Shell 脚本或 C 程序时,遵循 POSIX(可移植操作系统接口)标准所形成的常见做法和约定。它既包含语法/接口层面的规范,也包含行为和风格上的惯例。

下面从几个常见角度来说明。


1. 命令行选项的习惯

POSIX 对实用程序的命令行参数有明确约定,最典型的是:

  • 短选项用单个 -,如 -a、-l。
  • 短选项可以合并:ls -la 等价于 ls -l -a。
  • 选项和选项参数可以分开或连写:
    • grep -e pattern
    • grep -epattern(部分实现支持)
  • -- 表示选项结束:
    1
    rm -- -file
    用来删除名为 -file 的文件。
  • 长选项通常用 --,如 --help、--version,但这更多是 GNU 习惯,不是严格 POSIX。

POSIX 还规定了一些标准选项含义,例如:

  • -f:force / file
  • -i:interactive
  • -r:recursive(但并非所有命令都这样)
  • -v:verbose
  • -n:no-op / dry-run / numeric

2. 退出状态码的习惯

POSIX 程序通常遵循:

  • 0 表示成功
  • 非 0 表示失败
  • 1 通常表示一般错误
  • 2 常用于用法错误(如参数错误)
  • >128 通常表示被信号终止(如 130 = 128 + SIGINT)

Shell 中常见写法:

1
2
3
4
5
if command; then
echo \"success\"
else
echo \"failed with $?\"
fi

3. 标准输入/输出/错误

POSIX 习惯:

  • 正常输出到 stdout
  • 错误和诊断信息到 stderr
  • 程序应尽量支持管道和重定向

例如:

1
command > out.txt 2> err.txt

C 程序中:

1
2
fprintf(stdout, \"normal\\n\");
fprintf(stderr, \"error\\n\");

4. Shell 脚本中的 POSIX 习惯

如果写 #!/bin/sh 脚本,通常要遵循 POSIX sh,而不是 bash 扩展。

常见 POSIX 习惯:

  • 使用 $(...) 而不是反引号(反引号也可移植,但 $() 更清晰)
  • 变量引用加引号:
    1
    echo \"$var\"
  • 使用 [ ] 而不是 [[ ]]
  • 不使用数组、local、function 等 bash 扩展
  • 使用 command -v 检查命令是否存在:
    1
    2
    3
    if command -v git >/dev/null 2>&1; then
    ...
    fi
  • 使用 set -e 要谨慎,因为它在某些 POSIX 场景下行为微妙

一个典型的 POSIX 风格脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/bin/sh

set -u

usage() {
echo \"usage: $0 [-v] file\" >&2
exit 2
}

verbose=0

while getopts \"v\" opt; do
case \"$opt\" in
v) verbose=1 ;;
*) usage ;;
esac
done

shift $((OPTIND - 1))

if [ $# -ne 1 ]; then
usage
fi

file=$1

if [ \"$verbose\" -eq 1 ]; then
echo \"processing $file\"
fi

5. 文件与路径习惯

  • 路径分隔符是 /
  • 隐藏文件以 . 开头
  • 临时文件常放在 /tmp,并使用 mktemp
  • 配置常放在 /etc
  • 用户配置常以 ~/.xxx 或 ~/.config/xxx 形式存在

6. 文本流与行习惯

POSIX 工具通常按行处理文本:

  • 每行以 \\n 结尾
  • 工具如 grep、sed、awk、sort、cut 都面向行
  • 没有输出时通常不报错,只是无输出

例如:

1
grep pattern file

找到就输出,没找到退出码为 1。


7. C 程序中的 POSIX 习惯

  • 使用 open/read/write/close 而不是只依赖 C 标准库
  • 检查系统调用返回值
  • 处理 EINTR
  • 使用 fork/exec 而不是 system
  • 信号处理使用 sigaction 而不是 signal
  • 头文件如 <unistd.h>、<fcntl.h>、<sys/stat.h>

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

int main(void) {
int fd = open(\"file.txt\", O_RDONLY);
if (fd < 0) {
perror(\"open\");
return 1;
}

char buf[4096];
ssize_t n;
while ((n = read(fd, buf, sizeof buf)) > 0) {
if (write(STDOUT_FILENO, buf, n) < 0) {
perror(\"write\");
close(fd);
return 1;
}
}

if (n < 0) {
perror(\"read\");
close(fd);
return 1;
}

close(fd);
return 0;
}

8. “POSIX 习惯”常见的反面例子

  • 在 #!/bin/sh 里用 [[ ]]、数组、function、local
  • 把错误信息输出到 stdout
  • 成功时返回非 0,失败时返回 0
  • 不引用变量导致空格分词问题
  • 依赖 GNU 特有选项却声称可移植
  • 用 echo -e(POSIX echo 对 -e 的支持不一致)

总结

“POSIX 习惯”可以概括为:

  1. 接口可移植:使用 POSIX 标准定义的命令、选项、系统调用。
  2. 行为可预测:退出码、stdout/stderr、选项解析遵循约定。
  3. Shell 脚本克制:写 sh 就只用 POSIX sh 特性。
  4. 面向文本流:按行处理,支持管道和重定向。
  5. 错误处理明确:失败给非 0,诊断信息进 stderr。

如果你是在某个具体场景里看到“POSIX 习惯”这个词,比如写脚本、写 C 程序、还是命令行工具设计,可以告诉我,我可以针对那个场景展开。

Test 1

清除GitHub提交记录

Clear GitHub Commit History

📝 笔记总结

主要是有时候我的强迫症犯了:清除 GitHub 提交记录,核心是重写历史并强制推送。

📖 内容来源

清除 GitHub 提交记录,核心是重写历史并强制推送。操作前请务必备份,并注意:如果曾有人 Fork 或克隆过仓库,旧数据仍可能存在于他人本地或 GitHub 缓存中,无法从网络上彻底抹去。

方案一:清空所有历史,保留最新快照

如果你想让仓库看起来像全新的,只保留当前的文件状态,这是最简单的方法。

  1. 备份:将本地代码库复制到安全位置。
  2. 创建孤立分支:在项目目录下执行,这会创建一个没有父提交的全新分支。
    1
    git checkout --orphan new_branch
  3. 提交所有文件:把当前工作目录的所有文件加入并提交为一个新初始提交。
    1
    2
    git add -A
    git commit -m \"Initial commit (clean snapshot)\"
  4. 替换旧分支:删除原来的主分支(如 main 或 master),并把当前分支重命名为它。
    1
    2
    git branch -D main
    git branch -m main
  5. 强制推送:覆盖远程仓库的历史。
    1
    git push origin --force main

方案二:彻底删除 .git 目录后重新初始化

这个方法更直接,但会丢失所有分支配置和标签,适用于只想保留文件快照的场景。

  1. 删除 Git 仓库数据:进入项目目录,删除隐藏的 .git 文件夹。
    1
    rm -rf .git
  2. 重新初始化并提交:
    1
    2
    3
    4
    git init
    git add .
    git commit -m \"Initial commit\"
    git remote add origin <你的仓库地址>
  3. 强制推送:
    1
    git push --force --set-upstream origin main

方案三:删除特定文件或敏感提交

如果只是想删掉历史记录里的某个敏感文件(如密码、密钥),可以使用 git filter-repo 工具,它比旧版的 filter-branch 更快更安全。

  1. 安装:pip install git-filter-repo
  2. 删除文件:这会将指定文件从所有历史提交中移除。
    1
    git filter-repo --path 敏感文件名 --invert-paths
  3. 强制推送:git push origin --force --all(若涉及标签,也需推送 --tags)。

⚠️ 关键警告与善后

  • 秘密必须轮换:如果提交的是密码或密钥,立刻作废并重新生成。清除历史无法阻止别人在清除前已经拷贝,你必须默认它已经泄露。
  • 通知协作者:强制推送后,其他人的本地历史会与远程冲突。应通知他们重新克隆,而不是使用 git pull(合并)来更新,否则可能把旧历史又推回来。

Test

Test

Pelican 部署 GitHub 未生成文章

Pelican Has No Content

📝 笔记总结

  1. with: 部分添加 requirements 参数;
  2. 项目根目录创建 requirements.txt。

📖 内容来源

⚠️ 错误提示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# .github/workflows/pelican.yml
name: Deploy to GitHub Pages
on:
push:
branches: [\"main\"]
workflow_dispatch:
jobs:
deploy:
uses: \"getpelican/pelican/.github/workflows/github_pages.yml@main\"
permissions:
contents: \"read\"
pages: \"write\"
id-token: \"write\"
with:
settings: \"publishconf.py\"

使用官方的 pelican.yml,content 文件夹内有测试的文章 *.md,但是没有生成文章页面。

Pelican Build Pelican Site

💡 错误原因

Could not import ‘markdown.Markdown’. Have you installed the ‘markdown’ package?
GitHub Actions 环境中没有安装 Markdown 支持,所以 Pelican 无法解析 .md 文件,导致处理了 0 篇文章。

✅ 解决方法

with: 部分添加 requirements 参数

  • 指定需要安装的 Python 模块:
1
2
3
with:
settings: \"publishconf.py\"
requirements: \"pelican[markdown] typogrify markdown-include\"
  • 或者指定一个requirements文件: -r requirements.txt:
1
2
3
with:
settings: \"publishconf.py\"
requirements: \"-r requirements.txt\"
  • 完整的工作流文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
name: Deploy to GitHub Pages

on:
push:
branches: [\"main\"]
workflow_dispatch:

jobs:
deploy:
uses: \"getpelican/pelican/.github/workflows/github_pages.yml@main\"
permissions:
contents: \"read\"
pages: \"write\"
id-token: \"write\"
with:
settings: \"publishconf.py\"
requirements: \"-r requirements.txt\"

项目根目录创建 requirements.txt:

1
2
# 导出当前环境所有包
pip freeze > requirements.txt

典型的 Pelican 项目 requirements.txt 参考:

1
2
3
4
5
6
7
8
9
10
# Pelican 核心 + Markdown 支持
pelican[markdown]==4.12.0
# 排版优化(智能引号、连字符等)
typogrify==2.1.0
# 支持在 Markdown 中包含其他文件
markdown-include==0.8.1
# 代码高亮
pygments==2.17.2
# 同时支持 Markdown 和 reStructuredText
# pelican[markdown,html_rst]

SvelteKit 构建 GitHub Pages

GitHub Actions Free Minutes

📝 笔记总结

  1. 增加文件:svelte.config.js ;
  2. 增加文件:src/routes/+layout.js 。

📖 内容来源

💡错误提示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# .github/workflows/deploy.yml
name: Deploy to GitHub Pages

on:
push:
branches: 'main'

jobs:
build_site:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7

# If you're using pnpm, add this step then change the commands and cache key below to use `pnpm`
# - name: Install pnpm
# uses: pnpm/action-setup@v6
# with:
# version: 8

- name: Install Node.js
uses: actions/setup-node@v6
with:
node-version: 20
cache: npm

- name: Install dependencies
run: npm i

- name: build
env:
BASE_PATH: '/${{ github.event.repository.name }}'
run: |
npm run build

- name: Upload Artifacts
uses: actions/upload-pages-artifact@v5
with:
# this should match the `pages` option in your adapter-static options
path: 'build/'

deploy:
needs: build_site
runs-on: ubuntu-latest

permissions:
pages: write
id-token: write

environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}

steps:
- name: Deploy
id: deployment
uses: actions/deploy-pages@v5

使用官方的 deploy.yml ,运行失败,下面提示:

SvelteKit GitHub Pages Upload Artifacts

错误表明 GitHub Actions 在运行 actions/upload-pages-artifact@v5 时,找不到 build/ 目录。

本地构建文件都生成在 .svelte-kit/output/server/ 目录下,而不是 build 文件夹,说明 adapter-static 可能没有正确执行,或者构建过程在预渲染阶段失败了。

🚀 官方方法

使用 npm i -D @sveltejs/adapter-static 安装,然后将适配器添加到你的 svelte.config.js 中。

  1. 增加文件:svelte.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

import adapter from '@sveltejs/adapter-static';

export default {
kit: {
adapter: adapter({
// default options are shown. On some platforms
// these options are set automatically — see below
pages: 'build',
assets: 'build',
fallback: undefined,
precompress: false,
strict: true
})
}
};
  1. 增加文件:src/routes/+layout.js
1
2
// This can be false if you're using a fallback (i.e. SPA mode)
export const prerender = true;

💎 解决结果

文件生成在 .svelte-kit/output/server/ 和 build 文件夹。

SvelteKit npm run build

GitHub Actions 免费额度

GitHub Actions Free Minutes

📝 笔记总结

今天发现私有仓库的 Actions 运行失败了,下面提示:

The job was not started because recent account payments have failed or your spending limit needs to be increased. Please check the ‘Billing & plans’ section in your settings

我查询了一下,原来是免费额度用完了,顺便多了解了下。

GitHub Actions Free Minutes Annotations

查询:Settings → Billing and plans → Overview

页面中有一个 "Usage by products" 区域,点击 Actions 标签。

额度不够,是不是设置一个小号或者组织差不多够用。

📖 内容来源

GitHub Actions 的免费额度主要根据仓库是公开还是私有以及账户订阅计划来决定。核心原则是:公开仓库完全免费,私有仓库有每月额度限制。

把不同计划的免费额度表格查看:

账户计划 免费分钟数/月 (私有仓库) 免费存储空间 (工件与缓存)
GitHub Free (个人) 2,000 分钟 500 MB
GitHub Free (组织) 2,000 分钟 500 MB
GitHub Pro 3,000 分钟 1 GB
GitHub Team 3,000 分钟 2 GB
GitHub Enterprise Cloud 50,000 分钟 50 GB

提示:表格中的存储额度是工件(Artifacts) 和缓存(Cache) 共享的总和。缓存每个仓库还有额外的 10 GB 限制。

💡 超限后会发生什么?

  • 如果设置了支出限额:一旦用完免费额度,超出的部分会按标准费率(Linux 约 $0.008/分钟)计费,直到设置的限额为止。
  • 如果未设置支出限额(限额为 0):私有仓库的 Actions 会停止运行,直到下一个计费周期重置。

🚀 一些关键细节和限制

  • 工作流并发限制:GitHub Free 计划最多允许 20 个作业并发运行。如果超出,作业会进入排队状态。
  • 单次作业超时:GitHub 托管运行器上的单个作业最多运行 6 小时,超时后会被自动取消。
  • 存储扣费逻辑:存储费用是按小时计费并累积的。删除文件只能阻止未来继续计费,但无法免除文件在留存期间已经产生的费用。
  • 账户计费独立:个人账户和组织的付费额度是分开的,为组织付款不会覆盖到个人账户。
  • Copilot 代码评审:这项功能(如果启用)在私有仓库中也会消耗 Actions 分钟数。

💎 总结与建议

如果是开源项目或个人学习,公开仓库是零成本的最佳选择。如果必须使用私有仓库,建议密切关注仪表盘上的用量,并根据实际情况设置支出限额,防止意外超支。

0%