UNIX系统复习-4
这是我复习《UNIX系统》这门课程时记录的一些笔记 ,希望能对你有所帮助😊
Shell编程
一、Shell编程的意义
将命令组合成实用工具
- 示例:创建显示目录的脚本
1
2
3
4
5
6# lsdir脚本内容
ls -l | sed -n '/^d/p'
# 使用方式
chmod +x lsdir
./lsdir
- 示例:创建显示目录的脚本
快速编写实用软件
- 示例:带行号的文件查看器
1
2
3
4
5
6# mycat脚本内容
awk '{print NR, ": ", $0}' $1
# 使用方式
chmod +x mycat
./mycat filename
- 示例:带行号的文件查看器
二、位置参数
基本位置参数
$0
:脚本名称$1
,$2
, …:第1、2个参数等$#
:参数个数$*
:所有参数集合
参数重置与移动
- 使用
set
重置参数:1
set new1 new2 # 重置$1为new1,$2为new2
- 使用
shift
移动参数:1
2shift # 左移1位,丢弃$1
shift 2 # 左移2位
- 使用
三、Shell命令行结构
命令组合
- 顺序执行:
cmd1; cmd2
- 管道:
cmd1 | cmd2
- 后台执行:
cmd &
- 顺序执行:
命令分组
- 子shell执行:
(cmd1; cmd2)
- 当前shell执行:
{ cmd1; cmd2; }
- 子shell执行:
四、命令行模式与元字符
元字符 | 功能描述 |
---|---|
> | 输出重定向 |
>> | 追加输出 |
< | 输入重定向 |
| | 管道 |
* | 通配符 |
? | 单字符匹配 |
[a-z] | 字符范围匹配 |
; | 命令分隔符 |
& | 后台执行 |
` | 命令替换 |
$() | 命令替换(推荐) |
# | 注释 |
五、引号使用
单引号:完全原样输出
1
echo '$USER `date`' # 输出: $USER `date`
双引号:允许变量和命令替换
1
echo "$USER `date`" # 输出当前用户和日期
反引号:命令替换(已过时,推荐使用$())
1
2echo "Today is `date`"
echo "Today is $(date)" # 推荐写法
六、重定向
标准重定向
1
2
3cmd > file # 输出重定向
cmd >> file # 追加输出
cmd < file # 输入重定向错误重定向
1
2
3cmd 2> error.log # 错误输出到文件
cmd > output.log 2>&1 # 标准和错误都输出到文件
cmd &> all_output.log # 同上(更简洁)Here Document
1
2
3
4cat <<EOF
多行文本
可以直接输入
EOF
七、Shell程序结构
- 基本结构:命令序列
- 主要命令类型:
- 普通命令
- 赋值命令
- 运算命令
- 流程控制(if/case/for/while)
八、流程控制
if语句
1
2
3
4
5
6
7if [ condition ]; then
commands
elif [ condition ]; then
commands
else
commands
ficase语句
1
2
3
4
5case $var in
pattern1) commands ;;
pattern2) commands ;;
*) default commands ;;
esacfor循环
1
2
3
4
5
6
7
8
9# 形式1
for var in list; do
commands
done
# 形式2(C风格)
for ((i=0; i<10; i++)); do
commands
donewhile循环
1
2
3while [ condition ]; do
commands
doneuntil循环
1
2
3until [ condition ]; do
commands
done
九、test命令与条件判断
test
文件测试
1
2
3
4[ -e file ] # 文件存在
[ -f file ] # 是普通文件
[ -d file ] # 是目录
[ -s file ] # 文件非空字符串测试
1
2
3[ -z "$str" ] # 字符串为空
[ -n "$str" ] # 字符串非空
[ "$a" = "$b" ] # 字符串相等数值比较
1
2
3
4[ $a -eq $b ] # 等于
[ $a -ne $b ] # 不等于
[ $a -gt $b ] # 大于
[ $a -lt $b ] # 小于
十、Shell内部变量
变量 | 描述 |
---|---|
$? | 上条命令退出状态 |
$$ | 当前shell的PID |
$! | 最后后台进程PID |
$# | 参数个数 |
$@ | 所有参数(保留引号) |
$* | 所有参数(不保留引号) |
$_ | 上条命令的最后一个参数 |
十一、实用示例
查找命令路径(mywhich)
1
2
3
4
5
6
7
8
9
10
11
12
13
14#!/bin/bash
case $# in
0) echo "Usage: $0 cmd"; exit 1 ;;
esac
IFS=:
for dir in $PATH; do
if [ -x "$dir/$1" ]; then
echo "$dir/$1"
exit 0
fi
done
echo "$1: not found"
exit 1倒序显示参数
1
2
3
4#!/bin/bash
for ((i=$#; i>0; i--)); do
echo ${!i} # 间接引用
done
十二、Shell编程优缺点
优点:
- 快速组合现有命令
- 无需编译,修改方便
- 适合系统管理任务
缺点:
- 执行效率较低
- 复杂逻辑可读性差
- 可移植性受限
最佳实践建议
- 总在第一行指定解释器:
#!/bin/bash
- 使用
$()
代替反引号进行命令替换 - 变量引用加双引号:
"$var"
- 使用
[[ ]]
代替[ ]
进行条件测试(更强大) - 为脚本添加帮助信息和错误处理
- 复杂脚本考虑使用Python等更强大的语言
通过掌握这些核心知识,您可以编写出高效、可靠的Shell脚本,完成各种系统管理和自动化任务。
C语言开发环境
一、概述
C语言与UNIX的关系
- 1973年由Dennis Ritchie发明,用于重写UNIX系统
- 成为UNIX系统的”自然”语言
- 1988年IEEE推出POSIX标准,统一了C语言头文件
C语言优势
- 高效性:直接操作硬件
- 可移植性:POSIX标准确保跨平台兼容性
- 广泛应用:系统编程、嵌入式开发首选语言
二、C程序实例分析
1. 基本结构
1 |
|
2. 关键语法解析
注释
/* ... */
:多行注释//
:单行注释(C99标准)
预编译指令
#include <头文件>
:从系统路径(/usr/include)加载#include "头文件"
:从当前目录加载
主函数
- 必须的入口函数
- 参数:
argc
:参数个数argv[]
:参数数组(argv[0]为程序名)
- 返回值:
- 0表示成功
- 非0表示错误(便于Shell脚本判断)
三、C编译过程
1. 四个阶段
预处理
- 处理
#include
、#define
等指令 - 生成
.i
文件
- 处理
编译
- 词法/语法分析
- 生成汇编代码(.s文件)
汇编
- 将汇编代码转为机器码
- 生成目标文件(.o)
链接
- 合并多个目标文件
- 生成可执行程序
2. 常用GCC命令
命令 | 功能 |
---|---|
gcc hello.c | 生成a.out |
gcc -o hello hello.c | 指定输出文件名 |
gcc -c file.c | 仅编译生成.o文件 |
gcc -g program.c | 加入调试信息 |
四、调试工具GDB
1. 核心功能
- 设置断点
- 单步执行
- 查看变量值
- 修改程序状态
2. 常用命令
命令 | 功能 |
---|---|
gdb ./a.out | 启动调试 |
break main | 在main函数设断点 |
run | 运行程序 |
print x | 查看变量x的值 |
next | 单步执行(不进入函数) |
quit | 退出GDB |
五、工程管理工具Make
1. Makefile规则
1 |
|
2. 示例
1 |
|
3. 重要概念
变量
1
2
3OBJS = main.o utils.o
app: $(OBJS)
gcc -o app $(OBJS)隐含规则
- 自动推导依赖关系
- 如
.o
文件自动从.c
文件生成
伪目标
- 用于非编译任务(如clean)
- 需用
.PHONY
声明
六、标准C函数
1. 系统调用 vs 库函数
特性 | 系统调用 | 库函数 |
---|---|---|
提供者 | 操作系统内核 | 编程语言 |
功能 | 基础接口 | 高级封装 |
示例 | write() | printf() |
可替换性 | 不可替换 | 可替换 |
2. UNIX体系结构
1 |
|
3. 标准化
ISO C
- C90(1989)、C99(1999)
- 定义24个标准头文件
POSIX
- 增强UNIX系统可移植性
- 定义26个必需头文件
SUS
- 单一UNIX规范
- 扩展POSIX功能
系统调用
一、文件I/O(文件操作)
1. 文件描述符(File Descriptor, fd)
- 定义:非负整数,用于标识进程打开的文件。
- 默认文件描述符:
0
:标准输入(STdin_FILENO
,键盘输入)。1
:标准输出(STDOUT_FILENO
,屏幕输出)。2
:标准错误(STDERR_FILENO
,屏幕错误输出)。
示例:读取标准输入并写入标准输出
1 |
|
解释:
read(fd, buf, size)
:从fd
读取最多size
字节到buf
,返回实际读取字节数。write(fd, buf, size)
:将buf
的size
字节写入fd
。
2. open()
和 creat()
open()
函数
1 |
|
- **
flags
**:O_RDONLY
:只读。O_WRONLY
:只写。O_RDWR
:读写。O_CREAT
:文件不存在时创建。O_TRUNC
:若文件存在,清空内容。O_APPEND
:追加写入(避免并发问题)。
示例:打开/创建文件
1 |
|
解释:
O_CREAT | O_TRUNC
:若文件不存在则创建,若存在则清空。0644
:文件权限(rw-r--r--
)。
3. lseek()
:移动文件指针
1 |
|
- **
whence
**:SEEK_SET
:从文件开头计算偏移。SEEK_CUR
:从当前位置计算偏移。SEEK_END
:从文件末尾计算偏移。
示例:读取文件第 10 字节
1 |
|
4. dup2()
:文件描述符重定向
1 |
|
- 作用:让
newfd
指向oldfd
的文件。
示例:将标准输出重定向到文件
1 |
|
文件属性及目录
一、文件属性(struct stat
)
1. 属性结构体
1 |
|
2. 获取文件属性
- **
stat()
**:通过路径获取属性。1
int stat(const char *pathname, struct stat *buf);
- **
fstat()
**:通过文件描述符获取属性。1
int fstat(int fd, struct stat *buf);
示例:打印文件大小
1 |
|
二、文件类型与权限
1. 文件类型判断宏
宏 | 文件类型 |
---|---|
S_ISREG(mode) | 普通文件(如 .txt ) |
S_ISDIR(mode) | 目录 |
S_ISCHR(mode) | 字符设备(如 /dev/tty ) |
S_ISBLK(mode) | 块设备(如 /dev/sda ) |
S_ISFIFO(mode) | 管道(FIFO) |
S_ISLNK(mode) | 符号链接 |
S_ISSOCK(mode) | 套接字 |
示例:判断文件类型
1 |
|
2. 文件权限
- 9 个权限位:
- 用户:
S_IRUSR
(读)、S_IWUSR
(写)、S_IXUSR
(执行)。 - 组:
S_IRGRP
、S_IWGRP
、S_IXGRP
。 - 其他:
S_IROTH
、S_IWOTH
、S_IXOTH
。
- 用户:
- 特殊权限位:
S_ISUID
:执行时设置有效用户 ID(如passwd
)。S_ISGID
:执行时设置有效组 ID。S_ISVTX
:粘滞位(目录下文件仅所有者可删)。
修改权限
1 |
|
三、文件操作
1. 创建新文件
- 权限计算:
实际权限 = mode & ~umask
(umask
默认为022
,屏蔽组和其他的写权限) - 示例:
1
2umask(002); // 设置 umask
int fd = open("new.txt", O_CREAT | O_WRONLY, 0666); // 实际权限:664
2. 硬链接与删除
- **
link()
**:创建硬链接(增加st_nlink
)。1
link("old.txt", "new.txt"); // new.txt 指向 old.txt
- **
unlink()
**:删除链接(st_nlink--
,若为 0 则删除文件)。1
unlink("file.txt"); // 删除文件(需无进程打开)
- **
remove()
**:等价于unlink()
(文件)或rmdir()
(目录)。
3. 重命名
1 |
|
四、目录操作
1. 创建与删除目录
1 |
|
2. 读取目录内容
1 |
|
struct dirent
关键字段
d_name
:文件名。d_type
:文件类型(如DT_REG
普通文件)。
五、工作目录管理
1. 改变工作目录
1 |
|
2. 获取当前目录
1 |
|
六、总结表
功能 | 函数/宏 | 说明 |
---|---|---|
获取属性 | stat() , fstat() | 填充 struct stat |
文件类型判断 | S_ISREG() , S_ISDIR() | 检查 st_mode |
修改权限 | chmod() , fchmod() | 设置 rwx 权限 |
创建文件 | open() with O_CREAT | 受 umask 影响 |
硬链接操作 | link() , unlink() | 修改 st_nlink |
目录操作 | mkdir() , opendir() | 创建/遍历目录 |
工作目录 | chdir() , getcwd() | 类似 cd 和 pwd |
关键点
st_mode
是核心字段:包含文件类型和权限。- 权限计算:
实际权限 = mode & ~umask
。 - 硬链接 vs 符号链接:
- 硬链接:同一文件,共享 inode。
- 符号链接:独立文件,存储目标路径。
- 目录操作:只有内核可写目录,用户通过
opendir/readdir
读取。
建议:结合 ls -l
命令和 stat
命令验证文件属性。
二、进程控制
1. fork()
:创建子进程
1 |
|
- 返回值:
>0
:父进程,返回子进程 PID。=0
:子进程。-1
:出错。
示例:父子进程执行不同任务
1 |
|
输出:
1 |
|
2. exec()
:替换进程映像
1 |
|
- 作用:用新程序替换当前进程(如
ls
、gcc
)。
示例:子进程执行 ls -l
1 |
|
exec
函数族对比表
以下是 UNIX/Linux 中 exec
系列函数的详细对比,包括参数、使用场景和区别:
函数 | 参数形式 | 环境变量 | 查找路径 | 典型用途 |
---|---|---|---|---|
execl | execl("路径", "arg0", "arg1", ..., (char *)0) | 继承当前 | 需完整路径 | 已知可执行文件路径,参数列表固定 |
execv | execv("路径", char *argv[]) (argv 需以 NULL 结尾) | 继承当前 | 需完整路径 | 参数列表动态(如数组) |
execle | execle("路径", "arg0", ..., (char *)0, char *envp[]) | 自定义 | 需完整路径 | 需指定环境变量 |
execve | execve("路径", char *argv[], char *envp[]) (argv 和 envp 需以 NULL 结尾) | 自定义 | 需完整路径 | 系统调用,最底层实现 |
execlp | execlp("文件名", "arg0", ..., (char *)0) | 继承当前 | 从 PATH 查找 | 直接使用文件名(如 ls ) |
execvp | execvp("文件名", char *argv[]) (argv 需以 NULL 结尾) | 继承当前 | 从 PATH 查找 | 动态参数 + PATH 查找 |
关键区别
参数传递方式:
l
后缀(如execl
):参数以可变参数列表(arg0, arg1, ..., NULL
)传递。v
后缀(如execv
):参数以指针数组(argv[]
)传递,数组必须以NULL
结尾。
环境变量:
e
后缀(如execle
):可自定义环境变量(通过envp[]
数组)。- 无
e
后缀:继承当前进程的环境变量。
路径查找:
p
后缀(如execlp
):自动从PATH
环境变量查找可执行文件。- 无
p
后缀:必须提供完整路径(如/bin/ls
)。
底层实现:
execve
是唯一的系统调用,其他函数均为库函数(最终调用execve
)。
示例代码
1. execl
(固定参数 + 完整路径)
1 |
|
2. execv
(动态参数 + 完整路径)
1 |
|
3. execle
(自定义环境变量)
1 |
|
4. execlp
(自动查找 PATH
)
1 |
|
5. execvp
(动态参数 + 自动查找)
1 |
|
总结
- 需要完整路径 → 用
execl
、execv
、execle
、execve
。 - 需要
PATH
查找 → 用execlp
、execvp
。 - 动态参数 → 用
execv
、execvp
、execve
。 - 自定义环境变量 → 用
execle
、execve
。
注意:所有 exec
函数成功时不返回,失败时返回 -1
并设置 errno
。
3. wait()
:回收子进程
1 |
|
- 作用:父进程等待子进程结束,避免僵尸进程。
示例:父进程等待子进程
1 |
|
三、进程间通信(IPC)
1. 管道(pipe
)
1 |
|
- **
fd[0]
**:读端。 - **
fd[1]
**:写端。
示例:父子进程通信
1 |
|
输出:
1 |
|
- 再给一个例子,好好理解
1 |
|
1 |
|
四、信号(Signal)
1. signal()
:注册信号处理函数
1 |
|
- 常见信号:
SIGINT
(Ctrl+C
)。SIGKILL
(强制终止)。SIGALRM
(定时器)。
示例:捕获 Ctrl+C
1 |
|
运行:
1 |
|
总结
主题 | 关键函数 | 用途 |
---|---|---|
文件I/O | open , read , write , lseek , dup2 | 文件操作、重定向 |
进程控制 | fork , exec , wait | 创建进程、执行程序 |
IPC | pipe , mkfifo | 进程间通信 |
信号 | signal , kill , alarm | 异步事件处理 |
建议:
- 结合代码示例动手实验,加深理解。
- 使用
strace
观察系统调用执行过程(如strace ./a.out
)。
UNIX系统复习-4
http://pzhwuhu.github.io/2025/05/31/UNIX_Review_4/