Linux系统编程

Shell

命令解释器,根据舒服的命令执行相应命令

查看当前系统下有哪些shell

1
cat /etc/shells

查看当前系统正在使用的shell

1
echo $SHELL

常见shell

1
2
3
4
5
6
7
/bin/bash(就是linux的默认的shell)
/bin/csh(已经被/bin/tcsh所取代)
/bin/dash
/bin/ksh(Kornshell由AT&T Bell lab发展出来的,兼容于bash)
/bin/sh(已经被/bin/bash所取代)
/bin/tcsh(整合C Shell,提供更多功能)
/bin/zsh(基于ksh发展出来的,功能更强大的shell)

主键功能

功能 快捷键 助记
Home Ctrl-a The first letter
End Ctrl-e end
Clear Ctrl-u Clear

类Unix系统目录

1
2
3
4
5
6
7
8
bin:存放二进制可执行文件
boot:存放开机启动程序
dev:存放设备文件
home:存放用户
etc:用户信息和系统配置文件
lib:库文件
root:管理员宿主目录
usr:用户资源管理目录

Linux系统文件类型

1
2
3
4
5
6
7
8
普通文件:-
目录文件:d
字符设备文件:c
块设备文件:b
软连接:l
管道文件:p
套接字:s
未知文件

软链接:为保证软链接可以任意搬移,创建时务必对源文件使用绝对路径

硬链接:操作系统给每一个文件赋予唯一的inode,当有相同inode的文件存在时,彼此同步。删除时,只将硬链接计数减一。减为0时,inode被释放。

find

1
2
3
4
5
6
7
8
-type 按文件类型搜索
-name 按文件名搜索
-maxdepth 指定搜索深度
-size 按文件大小搜索,单位:k、M、G
-atime、mtime、ctime
-exec 将find搜索的结果集执行某一指定命令
-ok 以交互式的方式将find搜索的结果集执行某一指定命令
-xargs 将find搜索的结果集执行某一指定命令,当结果集数量过大时,可以分片映射

压缩解压缩

1
2
3
4
5
6
7
8
压缩
tar -zcvf 要生成的压缩包名 压缩材料
tar zcvf test.tar.gz file1 dir2 使用gzip方式压缩
tar jcvf test.tar.gz file1 dir2 使用bzip2方式压缩

解压
tar xcvf test.tar.gz 使用gzip方式解压
tar xcvf test.tar.gz 使用bzip2方式解压
1
2
3
4
rar

rar a -r newdir dir 打包:将dir压缩成newdir.dir
unrar x newdir.rar 解包:把newdir.rar解压缩到当前目录
1
2
3
4
zip

zip -r dir.zip dir 打包
zip dir.zip 解压

vim基础操作

进入插入模式

1
2
3
4
5
6
i:插入光标前一个字符
I:插入行首
a:插入光标后一个字符
A:插入行末
o:向下新开一行,插入行首
O:向上新开一行,插入行首

进入命令模式

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
58
59
60
61
62
63
64
65
66
ESC:从插入模式或末行模式进入命令模式
移动光标:
h:左移
j:下移
k:上移
l:右移

跳转到指定行
88G(命令模式)
88(末行模式)

跳转文件首
gg(命令模式)
跳转文件尾
G(命令模式)

自动格式化程序
gg=G(命令模式)

大括号对应
%(命令模式)

删除单个字符
x(命令模式)执行结束,工作模式不变
替换单个字符
将待替换的字符用光标选中,r(命令模式),再按欲替换的字符
删除一个单词
dw(命令模式)光标置于首字母进行操作
删除光标至行尾
D(命令模式)执行结束,工作模式不变
光标移至行首
0(命令模式)执行结束,工作模式不变
光标移至行尾
$(命令模式)执行结束,工作模式不变
删除光标至行尾
D或者d$(命令模式)
删除光标至行首
d0(命令模式)
删除指定区域
按V(命令模式)切换为“可视模式”,使用hjkl挪动光标来选中待删除区域,按d删除该区域数据
删除指定行
在光标所在行,按dd(命令模式)
删除指定N行
在光标所待删除首行,按Ndd(命令模式)
复制一行
yy
粘贴
p:向后,P:向前(粘贴位置为当前光标所在行的上一行或者下一行)
查找
找设想内容:命令模式下,按“/”输入欲搜索关键字,回车,使用n检索下一个
找看到的内容:命令模式下,将光标置于单词任意一个字符上,按“*” 或 “#”
单行替换
将光标置于待替换行上,进入末行模式,输入 :s /原数据/新数据
全局替换
将光标置于待替换行上,进入末行模式,输入 :%s /原数据/新数据/g,如果不加g只替换每行首个
指定行的替换
末行模式,:起始行号,终止行号s /原数据/新数据/g
撤销、反撤销
u、ctrl+r(命令模式)
分屏
sp:横屏分。Ctrl+ww切换
vsp:竖屏分。Ctrl+ww切换
跳转至man手册
将光标置于待查看函数单词上,使用K(命令模式)跳转。
查看宏定义
将光标置于待查看宏定义单词上,使用[d查看定义语句。

GCC编译

  • 预处理:展开宏、头文件,替换条件编译,删除注释、空白、空行
  • 编译:检查语法规范(消耗时间、系统资源最多)
  • 汇编:将汇编指令翻译成机器指令
  • 链接:数据段合并,地址回填
1
2
3
4
5
6
-I 指定头文件所在目录位置
-c 只做预处理、编译、汇编,得到二进制文件
-g 编译时添加调试文件,主要支持gdb调试
-Wall 显示所有警告信息
-E 生成预处理文件
-D 向程序中“动态注册宏定义”

静态库制作步骤

1
2
3
4
5
gcc -c test.c -o test.o
ar rcs lib库名.a test.o

编译静态库到可执行文件中
gcc test.c lib库名.a -o test

动态库制作步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
gcc -c test.c -o test.o -fPIC
gcc -shared -o lib库名.so test.o
编写可执行程序时,指定所使用的动态库。-l:指定库名 -L:指定库路径
gcc test.c -o a.out -l 库名 -L ./lib
运行可执行程序
./a.out(执行出错)
原因
链接器:工作于链接阶段,工作时需要-l和-L
动态链接器:工作于程序运行阶段,工作时需要提供动态库所在目录位置
通过环境变量:export LD_LIBRARY_PATH=动态库路径(临时生效,终端重启后环境变量失效)
1.永久生效:写入终端配置文件 vim ~/.bashrc
2.拷贝自定义动态库到 /lib(标准C库所在目录位置)
3.配置文件法
1.sudo vi /etc/ld.so.conf
2.写入动态库绝对路径 保存
3.sudo ldconfig -v 使配置文件生效
4.执行./a.out 使用ldd a.out 查看

GDB调试

1
2
3
4
5
6
7
8
9
10
-g 使用该参数编译可执行文件,得到调试表
gdb a.out
list: list/l 列出源码,根据源码指定行号设置断点
b: b 20 在20行位置设置断点
run/r: 运行程序
n/next: 下一条指令(会越过函数)
s/step: 下一条指令(会进入函数)
p/print: p i 查看i变量的值
continue: 继续执行断点后续指令
quit: 推出gdb当前调试

其他指令

1
2
3
4
5
6
7
8
9
10
run: 使用run查找段错误出现位置
finish: 结束当前函数调用
set args: 设置main函数命令行参数
run 字符串1 字符串2 ...: 设置main函数命令行参数
info b: 查看断点信息表
b 20 if i=5: 设置条件断点
ptype: 查看变量类型
bt: 列出当前程序正存活着的栈帧
frame: 根据栈帧编号,切换栈帧
display: 设置跟踪变量

makefile

命名:Makefile / makefile

  • 1个规则

    目标:依赖条件

    • (一个tab缩进)命令

    目标的时间必须晚于依赖条件的时间,否则,更新目标

    依赖条件如果不存在,找寻新的规则去产生依赖

  • 2个函数

    • src = $(wildcard ./*.c):匹配当前工作目录下的所有.c文件,将文件名组成列表,赋值给变量src
    • $(patsubst %.c,%.o,$(src)):将参数3中,包含参数1的部分,替换为参数2

    clean: (没有依赖)

    -rm -rf $(obj) a.out-的作用是,删除不存在文件时,不报错,顺序执行结束。

  • 3个自动变量

    • $@:在规则的命令中,表示规则中的目标
    • $^:在规则的命令中,表示所有依赖条件
    • $<:在规则的命令中,表示第一个依赖条件
  • 模式规则

    1
    2
    %.o:%.c
    gcc -c $< -o %@
  • 静态模式规则

    1
    2
    $(obj):%.o:%.c
    gcc -c $< -o %@
  • 伪目标

    1
    .PHONY: clean ALL
  • 参数

    1
    2
    -n : 模拟执行make、make clean命令
    -f : 指定文件执行make命令

makefile实战

首先创建一个maketest文件夹,该文件夹的目录结构如下图所示

1

inc文件夹下head.h代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef _HEAD_H_
#define _HEAD_H_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

int add(int,int);
int sub(int,int);
int div1(int,int);
int mul(int,int);

#endif

obj文件夹下的文件是我们在编译时生成的。

src文件夹下的hello.c代码如下

1
2
3
4
5
6
7
8
9
10
11
12
#include "head.h"

int main(int argc,char *argv[])
{
int a=10;int b=5;
printf("%d+%d=%d\n",a,b,add(a,b));
printf("%d-%d=%d\n",a,b,sub(a,b));
printf("%d/%d=%d\n",a,b,div1(a,b));
printf("%dx%d=%d\n",a,b,mul(a,b));

return 0;
}
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
add.c

int add(int a,int b)
{
return a+b;
}

div1.c

int add(int a,int b)
{
return a+b;
}

sub.c

int sub(int a,int b)
{
return a-b;
}

mul.c

int mul(int a,int b)
{
return a*b;
}

接下来是编写makefile文件,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
src = $(wildcard ./src/*.c)
obj = $(patsubst ./src/%.c,./obj/%.o,$(src))

inc_path = ./inc

ALL:a.out

a.out:$(obj)
gcc $^ -o $@

$(obj):./obj/%.o:./src/%.c
gcc -c $< -o $@ -I $(inc_path)

clean:
-rm -rf $(obj) a.out

.PHONY: clean ALL

最后一步执行makefile文件,在maketest目录下,执行make命令。就会在当前目录下生成a.out文件,同时在obj文件夹下也会生成相应的.o文件。

制作特定的makefile管理文件,代码如下,将代码放在指定的目录下,可以对指定的.c文件进行管理。执行命令的方式为make xxx,即可使得xxx.c文件通过gcc编译之后输出为xxx可执行目标文件。如果只执行make指令,则会对当前目录下的所有.c文件进行gcc编译。

1
2
3
4
5
6
7
8
9
10
11
12
src = $(wildcard *.c)
target = $(patsubst %.c,%,$(src))

ALL:$(target)

%:%.c
gcc $< -o $@

clean:
-rm -rf $(target)

.PHONY: clean ALL

函数相关

open函数
1
int open(char *pathname,int flags)

参数

1
2
3
pathname:要打开的文件路径名
flags: 文件打开方式 O_RDONLY|O_WRONLY|O_RDWR
O_CREAT|O_APPEND|O_EXCL|O_NONBLOCK

返回值

  • 成功:打开文件所得到对应的文件描述符(整数)
  • 失败:-1,设置error
1
int open(char *pathname,int flags, mode_t mode)

参数

1
2
3
4
5
pathname:要打开的文件路径名
flags: 文件打开方式 O_RDONLY|O_WRONLY|O_RDWR
O_CREAT|O_APPEND|O_EXCL|O_NONBLOCK
mode: 参数3使用的前提是,参数2指定了O_CREAT,取值8进制数,用来描述文件的访问权限。
创建文件最终权限 = mode & ~umask

返回值

  • 成功:打开文件所得到对应的文件描述符(整数)
  • 失败:-1,设置errno
close函数
1
int close(int fd)
read函数
1
ssize_t read(int fd,void *buf, size_t count);

参数

1
2
3
fd : 文件描述符
buf : 存数据的缓冲区
count : 缓冲区大小

返回值

1
2
成功:读到的字节数
失败:-1,设置errno
write函数
1
ssize_t write(int fd,const void *buf, size_t count);

参数

1
2
3
fd : 文件描述符
buf : 存数据的缓冲区
count : 数据大小

返回值

1
2
成功:写入的字节数
失败:-1,设置errno
错误处理函数
1
2
3
4
5
6
7
printf("xxx error: %d\n",errno);

char *strerror(int errnum);
printf("xxx error:%s\n",strerror(errno));

void perror(const char *s);
perror("open error");
fcntl
1
2
3
4
int flgs = fcntl(fd,G_GETFL);

获取文件状态:F_GETFL
设置文件状态:F_SETFL
read和write实现文件拷贝
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
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
int main(int argc,char *argv[])
{
char buf[1024];
int n = 0;

int fd1 = open(argv[1],O_RDONLY);
if (fd1 == -1){
perror("open argv1 error");
exit(1);
}
int fd2 = open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0664);
if (fd2 == -1){
perror("open argv2 error");
exit(1);
}
while ((n = read(fd1,buf,1024))!=0){
if (n<0){
perror("read error");
exit(1);
}
write(fd2,buf,n);
}

close(fd1);
close(fd2);
return 0;
}
lseek函数
1
off_t lseek(int fd,off_t offset, int whence);

参数

1
2
3
fd: 文件描述符
offset: 偏移量
whence: 起始偏移位置: SEEK_SET/SEEK_CUR/SEEK_END

返回值

1
2
成功:较起始位置偏移量
失败:-1 errno

使用lseek函数读文件

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
#include<stdio.h>
#include<fcntl.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>

int main(void)
{
int fd,n;
char msg[] = "It's a test for lseek\n";
char ch;

fd = open("lseek.txt",O_RDWR|O_CREAT,0644);
if(fd<0){
perror("open lseek.txt error");
exit(1);
}

write(fd,msg,strlen(msg));

lseek(fd,0,SEEK_SET); //将文件偏移量设为0,从最开始读

while((n = read(fd,&ch,1))){
if(n<0){
perror("read error");
exit(1);
}
write(STDOUT_FILENO,&ch,n);
}
close(fd);

return 0;
}

lseek应用场景

  • 文件的“读”、“写”使用同一偏移位置

  • 使用lseek获取、拓展文件大小:要想使文件大小真正扩展,必须引起IO操作。

    • 使用truncate函数直接扩展文件大小
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include<stdio.h>
    #include<fcntl.h>
    #include<stdlib.h>
    #include<string.h>
    #include<unistd.h>
    #include<pthread.h>

    int main(int argc, char *argv[])
    {
    int ret = truncate("lseek.txt",500);
    printf("ret = %d\n",ret);
    return 0;
    }

使用lseek获取文件大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<stdio.h>
#include<fcntl.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>

int main(int argc,char *argv[])
{
int fd = open(argv[1],O_RDWR);
if(fd == -1)
{
perror("open error");
exit(1);
}

int length = lseek(fd,0,SEEK_END);
printf("file size:%d\n",length);

close(fd);
return 0;
}
1
2
od -tcx filename 查看文件的16进制表示形式
od -tcd filename 查看文件的10进制表示形式
传入参数
  • 指针作为函数参数
  • 通常有const关键字修饰
  • 指针指向有效区域,在函数内部做读操作
传出参数
  • 指针作为函数参数
  • 在函数调用之前,指针指向的空间可以无意义,但必须有效
  • 在函数内部,做写操作
  • 函数调用结束后,充当函数返回值
传入传出参数
  • 指针作为函数参数
  • 在函数调用之前,指针指向的空间有实际意义
  • 在函数内部,先做读操作,后做写操作
  • 函数调用结束后,充当函数返回值
stat函数
1
int stat(const char *path,struct stat *buf)

参数

1
2
path : 文件路径
buf : (传出参数)存放文件属性

返回值

1
2
成功:0
失败:-1 errno

stat结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct stat { /* when _DARWIN_FEATURE_64_BIT_INODE is NOT defined */
dev_t st_dev; /* device inode resides on */
ino_t st_ino; /* inode's number */
mode_t st_mode; /* inode protection mode */
nlink_t st_nlink; /* number of hard links to the file */
uid_t st_uid; /* user-id of owner */
gid_t st_gid; /* group-id of owner */
dev_t st_rdev; /* device type, for special file inode */
struct timespec st_atimespec; /* time of last access */
struct timespec st_mtimespec; /* time of last data modification */
struct timespec st_ctimespec; /* time of last file status change */
off_t st_size; /* file size, in bytes */
quad_t st_blocks; /* blocks allocated for file */
u_long st_blksize;/* optimal file sys I/O ops blocksize */
u_long st_flags; /* user defined flags for file */
u_long st_gen; /* file generation number */
};

stat函数获取文件大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<stdio.h>
#include<fcntl.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/stat.h>

int main(int argc,char *argv[])
{
struct stat sbuf;
int ret = stat(argv[1],&sbuf);
if(ret == -1){
perror("stat error");
exit(1);
}
printf("file size:%lld\n",sbuf.st_size);

return 0;
}

stat函数获取文件类型

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
#include<stdio.h>
#include<fcntl.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/stat.h>

int main(int argc,char *argv[])
{
struct stat sb;
int ret = stat(argv[1],&sb);
if(ret == -1){
perror("stat error");
exit(1);
}
if(S_ISREG(sb.st_mode)){
printf("It's a regular\n");
} else if(S_ISDIR(sb.st_mode)){
printf("It's a dir\n");
} else if(S_ISFIFO(sb.st_mode)){
printf("It's a pipe\n");
} else if(S_ISLNK(sb.st_mode)){
printf("It's a sym link\n");
}

return 0;
}

stat和lstat的区别

stat会穿透符号链接,lstat不会。

unlink函数
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
#include<stdio.h>
#include<fcntl.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/stat.h>

int main(int argc,char *argv[])
{
int fd,ret;
char *p = "test of unlink\n";
char *p2 = "after write something.\n";

fd = open("temp.txt",O_RDWR|O_CREAT|O_TRUNC,0644);
if (fd<0){
perror("open temp error");
exit(1);
}

ret = write(fd,p,strlen(p));
if(ret == -1){
perror("----write error");
}
printf("hi! I'm printf\n");
ret = write(fd,p2,strlen(p2));
if(ret == -1){
perror("----write error");
}

printf("Enter anykey continue\n");
getchar();

close(fd);
ret = unlink("temp.txt");
if (ret<0){
perror("unlink error");
exit(1);
}
return 0;
}

unlink函数的特征:清除文件时,如果文件的硬链接数到0了,没有dentry对应,但该文件仍不会马上被释放,要等到所有打开该文件的进程关闭该文件,系统才会挑时间将该文件释放掉。

隐式回收

当进程结束运行时,所有该进程打开的文件会被关闭,申请的内存空间会被释放,系统的这一特性称之为隐式回收系统资源。

readlink函数

读取符号链接文件本身内容,得到链接所指向的文件名。

1
2
3
4
ssize t readlink(const char *path,char *buf,size_t bufsiz);

成功:返回实际读到的字节数
失败:-1 设置errno为相应值
opendir
1
2
3
DIR *opendir(const char *name)

成功返回指向该目录结构体指针
closedir
1
2
3
4
int closedir(DIR *dirp)

成功:0
失败:-1 设置errno为相应值
readdir
1
2
3
4
5
6
struct dirent *readdir(DIR *dp)

struct dirent {
inode
char dname[256]
}
实现ls命令操作
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
#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<unistd.h>
#include<dirent.h>
#include<stdlib.h>


int main(int argc,char *argv[])
{
DIR *dp;
struct dirent *sdp;

dp = opendir(argv[1]);
if (dp == NULL) {
perror("opendir error");
exit(1);
}

while((sdp = readdir(dp))!= NULL){
if(strcmp(sdp->d_name,".") == 0)
continue;
printf("%s\t",sdp->d_name);
}
printf("\n");
closedir(dp);
return 0;
}
递归遍历目录

直接贴代码

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <stdlib.h>

#define MAX_PATH_SIZE 256

void r_read_dir(char* path);
bool is_dir(char* path);

void r_read_dir(char* path) {
if (!is_dir(path)) // 如果不是目录,直接输出文件名
printf("%s ", path);
else {
printf("%s:\n", path);

DIR *dirp;
dirp = opendir(path);
if (!dirp) { // dirp == NULL
perror("opendir error");
exit(EXIT_FAILURE);
}

struct dirent* direntry;
errno = 0;
while ((direntry = readdir(dirp))!=NULL) {
if(strcmp(direntry->d_name,".")==0)
continue;
if(strcmp(direntry->d_name,"..")==0)
continue;
if (direntry->d_type != DT_DIR)
printf("%s ", direntry->d_name); // 第一次打开,输出所有的文件名
}
if (!direntry && errno) { // direntry == NULL && errno != 0
perror("readdir error");
exit(EXIT_FAILURE);
}

if (closedir(dirp) == -1) {
perror("closedir error");
exit(EXIT_FAILURE);
}

dirp = opendir(path);
if (!dirp) { // dirp == NULL
perror("opendir error");
exit(EXIT_FAILURE);
}
errno = 0;
while ((direntry = readdir(dirp))!=NULL) {
if(strcmp(direntry->d_name,".")==0)
continue;
if(strcmp(direntry->d_name,"..")==0)
continue;
if (direntry->d_type == DT_DIR)
printf("%s ", direntry->d_name); // 第二次打开,输出所有的目录名
}
if (!direntry && errno) { // direntry == NULL && errno != 0
perror("readdir error");
exit(EXIT_FAILURE);
}

if (closedir(dirp) == -1) {
perror("closedir error");
exit(EXIT_FAILURE);
}

dirp = opendir(path);
if (!dirp) { // dirp == NULL
perror("opendir error");
exit(EXIT_FAILURE);
}
errno = 0;
while ((direntry = readdir(dirp))!=NULL) {
if(strcmp(direntry->d_name,".")==0)
continue;
if(strcmp(direntry->d_name,"..")==0)
continue;
if (direntry->d_type == DT_DIR) {
char new_path[MAX_PATH_SIZE] = "";
strcat(new_path, path);
strcat(new_path, "/");
strcat(new_path, direntry->d_name); // 拼接好新路径
printf("\n\n");
r_read_dir(new_path); // 递归
}
}
if (!direntry && errno) { // direntry == NULL && errno != 0
perror("readdir error");
exit(EXIT_FAILURE);
}

if (closedir(dirp) == -1) {
perror("closedir error");
exit(EXIT_FAILURE);
}
}
}

bool is_dir(char* path) {
struct stat statbuf;
if (stat(path, &statbuf) == -1) {
perror("stat error");
exit(EXIT_FAILURE);
}

if (S_ISDIR(statbuf.st_mode))
return true;
else
return false;
}
int main(int argc, char** argv) {

char path[MAX_PATH_SIZE] = "";

if (argc == 1) // 用户没有输入路径
strcat(path, "."); // 默认为当前路径
else
strcat(path, argv[1]);

r_read_dir(path);

return 0;
}

实现效果和linux原生大差不差

2

dup和dup2
1
2
3
4
int dup(int oldfd);

oldfd:已有文件描述符
返回:新文件描述符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>
#include<unistd.h>
#include<pthread.h>

int main(int argc,char *argv[])
{
int fd = open(argv[1],O_RDONLY);
int newfd = dup(fd);
printf("newfd = %d\n",newfd);
return 0;
}
1
2
3
4
int dup2(int oldfd,int newfd);

oldfd:已有文件描述符
newfd:新文件描述符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>
#include<unistd.h>
#include<pthread.h>

int main(int argc,char *argv[])
{
int fd1 = open(argv[1],O_RDWR);
int fd2 = open(argv[2],O_RDWR);

int fdret = dup2(fd1,fd2);
printf("fdret = %d\n",fdret);

int ret = write(fd2,"1234567",7);
printf("ret = %d\n",ret);

dup2(fd1,STDOUT_FILENO);
printf("---------------test\n");
return 0;
}
fcntl函数实现dup
1
2
3
4
int fcntl(int fd,int cmd,...)

cmd: F_DUPFD
参3: 被占用的,返回最小可用的。未被占用的,返回=该值的文件描述符