POSIX habits

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 程序、还是命令行工具设计,可以告诉我,我可以针对那个场景展开。