Linux编程学习笔记-Linux系统文件编程详解

Linux文件操作命令

ls命令

  • 功能:列出指定目录下的内容及其相关属性信息

  • 语法:ls [参数选项] [文件]

  • 常见参数:

    -a显示所有文件以及目录,包括'以"."开头的隐藏文件'
    -l使用长格式列出文件及目录信息
    -r将文件以相反次序显示(默认依英文字母次序)
    -t根据最后的修改时间排序
    -A显示所有文件以及目录,但不列出"."(当前目录)".."(父目录)
    -S根据文件大小排序
    -R递归列出所有子目录
  • 示例:

    # 列出文件详情
    coder@coder-vm:/$ ls -l
    # 列出当前工作目录下所有名称是"s"开头的文件,在ubuntu中还会将文件夹以及其中的文件信息
    coder@coder-vm:/$ ls -ltr s*
    # 列出指定目录下的详细信息包含子目录
    coder@coder-vm:/$ ls -lR /sys
    

cp命令

  • 功能:复制文件或目录

  • 语法:cp [参数选项] [文件1、文件2、…|文件夹] [目标地址]

  • 常见参数:

    -f若目标文件已存在,则会直接覆盖原文件
    -i若目标文件已存在,则会询问是否覆盖
    -p保留源文件或目录的所有属性
    -r递归复制文件和目录
    -d当复制符号连接时,把目标文件或目录也建立为符号连接,并指向与源文件或目录连接的原始文件或目录
    -l对源文件建立硬连接,而非复制文件
    -s对源文件建立硬连接,而非复制文件
    -b覆盖已存在的文件目标前将目标文件备份
    -v详细显示cp命令执行的操作过程
    -a等价于dpr选项
  • 示例:

    # 复制目录 -R 等价于 -r
    coder@coder-vm:~$ cp -R Code/ /Code_1/
    # 复制多个文件,Temp 要存在
    coder@coder-vm:~/Code$ cp -r CMakeLists.txt main.cpp ../Temp
    

mkdir命令

  • 功能:创建目录

  • 语法:mkdir [参数选项] [文件夹1、文件夹2、…]

  • 常见参数:

    -p递归创建多级目录
    -m建立目录的同时设置目录的权限
    -z设置安全上下文
    -v显示目录的创建过程
  • 示例:

    # 在指定位置创建文件夹
    coder@coder-vm:~$ mkdir dir
    # 递归创建文件夹
    coder@coder-vm:~$ mkdir -p dir/code/
    

mv命令

  • 功能:可以移动文件或对其改名

  • 语法:mv [参数选项] [源文件|源目录] [目标文件|目标目录]

  • 常见参数:

    -i若存在同名文件,则向用户询问是否覆盖
    -f覆盖已有文件时,不进行任何提示
    -b当文件存在时,覆盖前为其创建一个备份
    -u当源文件比目标文件新,或者目标文件不存在时,才执行移动此操作
  • 示例:

    # 文件重命名
    coder@coder-vm:~$ mv main.cpp hello.cpp
    # 剪切文件夹到指定目录下,指定目录提前创建
    coder@coder-vm:~$ mv Code/ Code_mv/
    

cat命令

  • 功能:查看文件内容

  • 语法:cat [参数选项] [文件]

  • 常见参数:

    -n显示行数,空行也编号
    -s显示行数,多个空行算一个编号
    -b显示行数,空行不编号
    -E每行结束处显示$符号
    -T将TAB字符显示为^|符号
    -v使用^M-引用,除了LFDTAB之外
    -e等价于-vE组合
    -t等价于-vT组合
    -A等价于-vET组合
    –help显示帮助信息
    –version显示版本信息
  • 示例:

    # 查看文件内容和行号
    coder@coder-vm:~$ cat -n Code/CMakeLists.txt
    

rm命令

  • 功能:删除一个目录中的一个或多个文件或目录

  • 语法:rm [参数选项] [文件|文件夹]

  • 常见参数:

    -f忽略不存在的文件,不会出现警告信息
    -i删除前会询问用户是否操作
    -r/-R递归删除
    -v显示指令的详细执行过程
  • 示例:

    # 删除前逐一询问确认
    coder@coder-vm:~$ rm -i Code/CMakeLists.txt
    # 递归删除目录及目录下所有文件
    coder@coder-vm:~$ rm -rf Code/
    

grep命令

  • 功能:基于正则表达式的文本搜索

  • 语法:grep [参数选项]

  • 常见参数:

    -i搜索时,忽略大小写
    -c只输出匹配行的数量
    -l只列出符合匹配的文件名,不列出具体的匹配行
    -n列出所有的匹配行,显示行号
    -s不显示不存在、没有匹配文本的错误信息
    -v显示不包含匹配文本的所有行
    -w匹配整词
    -x匹配整行
    -r递归搜索
    -q禁止输出任何结果,已退出状态表示搜索是否成功
    -b打印匹配行距文件头部的偏移量,以字节为单位
    -o打印匹配行距文件头部的偏移量,以字节为单位
    -h查询多文件时不显示文件名
  • 示例:

    # 多文件查询并支持使用通配符
    coder@coder-vm:~$ grep zwx file_* /etc/hosts
    file_1:zwx
    file_1:zwx
    file_1:zwxddkjflkdjfdlkfjlsdkj
    file_2:zwx
    file_4:dkfjlzwxejfkje
    file_4:zwx djfkdjf
    file_4:zwxedkfgj
    

more命令

  • 功能:用于将内容较长的文本文件内容进行分屏显示,并且支持在显示时定位关键字

  • 语法:grep [参数选项] [文件]

  • 常见参数:

    -num指定每屏显示的行数
    -lmore在通常情况下把^L当作特殊字符, 遇到这个字符就会暂停,-l选项可以阻止这种特性
    -f计算实际的行数,而非自动换行的行数
    -p先清除屏幕再显示文本文件的剩余内容
    -c-p相似,不滚屏,先显示内容再清除旧内容
    -s多个空行压缩成一行显示
    -u禁止下划线
    +/pattern在每个文档显示前搜寻该字pattern,然后从该字串之后开始显示
    +num从第num行开始显示
  • 内部命令

    Space键显示文本的下一屏内容
    B键显示文本的上一屏内容
    斜线符\向下n行,需要定义,默认为1行
    H键显示帮助屏
    Enter键向下n行,需要定义,默认为1行
    Q键退出more命令
    =输出当前的行号
    :f输出文件名和当前的行号
    V调用vi编辑器
    !调用Shell,并执行命令
  • 示例

    # 显示文件file的内容,显示之前先清屏,附已显示的百分比
    coder@coder-vm:~$ more -dc Code/CMakeLists.txt
    # 显示文件file的内容,每10行显示一次,而且在显示之前先清屏
    coder@coder-vm:~$ more -c -10 Code/CMakeLists.txt
    # 从第 20 行开始显示 file 之文档内容
    coder@coder-vm:~$ more +20 Code/CMakeLists.txt
    

Linux文件系统

在Linux中可以使用ls -l命令看到当前文件夹下全部文件或文件夹的属性信息:

coder@coder-vm:~/Code$ ls -l
-rw-rw-r-- 1 coder coder  110  116 09:50 CMakeLists_bk.txt

在每一个文件或者文件夹名称的前边详细描述了文件的信息,信息解析情况如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FcQfiG4M-1643122019252)(\image\文件属性信息解析.png)]

在Linux系统中内核为每一个新创建的文件分配一个i(索引 index node)节点。文件的属性信息就保存在索引节点里,访问文件时索引节点被复制到内存中,从而实现文件的快速访问。索引节点是Linux虚拟文件系统(Virtual File System VFS)的基本概念之一。

i节点不但包含某个文件属性信息,还包含指向存储文件数据的数据块的指针。在Linux中一个文件除了纯数据本身之外,还必须包含对这些纯数据的管理信息,这些管理信息称为元数据(mata data)保存在文件的i节点之中。因此,可以简单的将i节点看成一个结构体其中保存了一些文件信息:

  • 节点编号,每个i节点都有一个唯一的编号。
  • 文件类型。
  • 权限信息。
  • 文件的字节数。
  • 文件的拥有者uid。
  • 文件的所属组gid。
  • 文件的时间戳,i节点最近变动时间(ctime)内容最近变动时间(mtime)文件上次访问时间(atime)
  • 硬盘链接数。
  • 纯数据块的指针位置。

i节点的总数在格式化时给定,每个i节点大小一般为128字节或256字节,但实际是每1KB或2KB设置一个i节点,用df命令可以查看i节点总数已使用i节点数量,每次创建文件可用i节点就会减少1个,命令如下所示:

# 查看i节点信息
df -i
# 查看系统使用信息
df -h

在Linux中每个文件都有一个i节点号与之对应,可用通过命令查看某个文件的i节点号

coder@coder-vm:~/Code$ ls -i CMakeLists.txt 
1973171 CMakeLists.txt

这种i节点号虽然对应着具体文件,但i节点号不能被人所识别,人所能识别的就只有名称字符。因此,Linux系统中存在一个目录项概念,目录中包含了i节点号文件名对应关系,当用户打开文件时一共分为三个步骤:系统通过目录项找到这个文件名对应的i节点号通过i节点号获得节点信息根据节点信息找到文件块读取数据

Linux文件编程

在Linux中设备或者文件都是通过文件描述符进行,当打开或者创建一个文件的时候,内核像进程返回一个文件描述符(非负整数)并基于该文件描述符进行操作。

打开文件

  • 相关函数
#include <fcntl.h>
int open(const char * pathname, int flags);
int open(const char * pathname, int flags, mode_t mode);
  • 参数说明
参数名称说明
pathname文件路径名称,相对路径绝对路径
flags文件打开方式
mode用来规定文件的所有者、文件的用户组及系统中其他用户的访问权限
  • flags标志说明
标志位说明
O_RDONLY打开一个只供读取文件(单选)
O_WRONLY打开一个只供写入文件(单选)
O_RDWR打开一个可供读写的文件(单选)
O_APPEND数据写入时追加到文件末尾
O_CREAT打开文件时如果不存在就创建文件
O_EXCL在使用了O_CREAT字段情况下,文件存在文件打开失败
O_TRUNC打开文件时将文件清空
O_DSYNC每次写入时,等待数据写到磁盘上
O_RSYNC每次读取时,等待相同部分先写到磁盘
O_SYNC已同步方式写入文件,强制刷新缓冲到输出文件
  • mode标志说明
标志位说明
S_IRUSR文件所有者的读取权限位
S_IWUSR文件所有者的写入权限位
S_IXUSR文件所有者的执行权限位
S_IRWXUS_IRUSR|S_IWUSR|S_IXUSR
S_IRGRP文件用户组的读取权限位
S_IWGRP文件用户组的写入权限位
S_IXGRP文件用户组的执行权限位
S_IRWXGS_IRGRP|S_IWGRP|S_IXGRP
S_IROTH文件其他用户的读取权限位
S_IWOTH文件其他用户的写入权限位
S_IXOTH文件其他用户的执行权限位
S_IRWXOS_IROTH|S_IWOTH|S_IXOTH
  • 示例
#include <iostream>
#include <fcntl.h>

using namespace std;
int main() {
    // 使用读写模式打开当前 exe 同级目录下的 test_data.txt
    int fd = open("./test_data.txt", O_RDWR);
    if (fd != -1) {
        cout << "test file open success." << endl;
    } else {
        cout << "test file open fail." << endl;
    }
    return 0;
}

关闭文件

文件不在使用的时候就要将其关闭,关闭后文件描述不再指向任何文件,如果不关闭文件将会造成系统文件描述符资源耗尽,此时,不可再次打开文件。因此,打开文件和关闭文件应成对使用,文件关闭时返回一个整数其中最常见的0表示关闭成功-1表示关闭失败

  • 相关函数
#include <unistd.h>
int close(int fd);
  • 参数说明
参数名称说明
fd文件创建或打开后或返回一个文件描述符是一个整型变量
  • 示例
#include <iostream>
#include <fcntl.h>

using namespace std;
int main() {
    int fd = open("./test_data.txt", O_RDWR);

    if (fd != -1) {
        cout << "test file open success." << endl;
    } else {
        cout << "test file open fail." << endl;
        return -1;
    }
    close(fd);
    return 0;
}

读取文件

通过打开文件后返回文件描述符,可以读取文件中的数据,可以一次读取指定字节长度的内容到指定的缓存中,读取时返回值是实际读取的字节数,可通过目标读取字节实际读取字节的比较判断是否读取到文件的结尾。

  • 相关函数
#include <unistd.h>
ssize_t read(int fd, void * buf, size_t count);
  • 参数说明
参数名称说明
fd已经打开的文件描述符
buf读取到的文件会存放到buf内存地址中
count每次读取的文件数据的长度
  • 常见返回异常值
参数名称说明
EINTR此调用被信号所中断
EAGAIN当使用不可阻断I/O时或无数据可读
EBADF参数fd无效或者文件已经关闭
  • 示例
#include <iostream>
#include <fcntl.h>
#include <unistd.h>

using namespace std;

int main() {
    int fd = open("./test_data.txt", O_RDWR);
	// ........
    size_t read_size = 1024;
    char read_buffer[1024];		// 读取数据缓存区
    while(true) {
        size_t read_real_size = read(fd, read_buffer, read_size);
        if (read_real_size > 0) {
            cout << "read size " << read_real_size << endl;
        } else {
            break;
        }
    }
    cout << "read over" << endl;
    close(fd);
    return 0;
}

写入文件

通过打开文件后返回文件描述符,可以向文件中的写入数据,可以将指定的缓存中的内容写入到文件中。

  • 相关函数
#include <unistd.h>
ssize_t write(int fd, const void * buf, size_t count);
  • 参数说明
参数名称说明
fd已经打开的文件描述符
buf需要写入的数据的文件缓存
count每次写入文件数据的长度
  • 常见返回异常值
参数名称说明
EINTR此调用被信号所中断
EADF参数fd无效或者文件已经关闭
  • 示例
#include <iostream>
#include <cstring>
#include <fcntl.h>
#include <unistd.h>

using namespace std;

int main() {
    int fd = open("./test_data.txt", O_RDWR);
    // ........
    char write_buffer[] = "hello";
    ssize_t write_size = write(fd, write_buffer, strlen(write_buffer));
    close(fd);
    return 0;
}

文件偏移量

文件可以直接从指定位置开始读写数据,文件偏移量指的是当前文件操作位置相对于目标位置的偏移,当打开一个文件时在未指定O_APPEND情况下默认是0,如果指定O_APPEND则文件偏移到文件的结尾与文件的长度相等。

  • 相关函数
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
  • 参数说明
参数名称说明
fd已经打开的文件描述符
offset偏移的字节数该值与whence设置有关
whence偏移量的计算位置标记
  • whence标志说明
标志位说明
SEEK_SEToffset为相对文件开始处的值
SEEK_CURoffset为相对当前位置处的值
SEEK_ENDoffset为相对文件结尾处的值
  • 示例
#include <iostream>
#include <cstring>
#include <fcntl.h>
#include <unistd.h>

using namespace std;
int main() {
    int fd = open("test_data.txt", O_RDWR);
    // 文件内容: hello seek file code
    off_t result = lseek(fd, 5, SEEK_SET);
    if (result == -1) {
        cout << "seek fail." << endl;
        return -1;
    }
    char buffer[] = "Insert";
    ssize_t w_result = write(fd, &buffer, strlen(buffer));
    close(fd);
    return 0;
}

获取文件状态

文件的状态信息包含了例如,文件所有者、文件修改时间、文件大小等

  • 相关函数
#include <sys/stat.h>
int stat(const char * path, struct stat * buf);
int fstat(int filedes, struct stat * buf);
int lstat(const char * path, struct stat * buf);
  • 参数说明
参数名称说明
path文件完整路径
filedes文件描述符
bufstat结构体的指针

在三个函数中fstat需要一个已打开文件的描述符,即已经open过的文件,另外两个则是文件全路径。statlstat区别在于,当文件是一个符号链接时,前者返回链接指向文件的信息而后者返回链接文件的信息

  • stat结构体说明
变量类型变量名称变量说明
mode_tst_mode文件对应的模式
ino_tst_inoinode节点号
dev_tst_dev设备号码
dev_tst_rdev特殊设备号码
nlink_tst_nlink文件的链接数
uid_tst_uid文件所有者
gid_tst_gid文件所有者对应的组
off_tst_size文件对应的字节数[普通文本]
time_tst_atime文件最后被访问的时间
time_tst_mtime文件内容最后被修改的时间
time_tst_ctime文件状态改变的时间
blksize_tst_blksize文件内容对应的块大小
blkcnt_tst_blocks文件内容对应的块数量
  • 示例
#include <iostream>
#include <cstring>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>

using namespace std;

int main() {
    struct stat file_stat{};
    if (-1 == stat("test_data.txt", &file_stat)) {
        cout << "get stat fail." << endl;
        return -1;
    }
    // 文件长度
    cout << "文件长度 = " << file_stat.st_size << endl;
    // 文件节点号
    cout << "文件索引号 = " << file_stat.st_ino << endl;
    return 0;
}

文件锁定

可以通过给文件增加标记来避免共享的文件资源产生竞争状态,文件锁分为两种建议性锁强制性锁,前者只是给文件加上锁标识其他进程可以读取到该标识但不会真的阻止文件操作,后者则是在其他进程访问时组织相应的操作。

  • 相关函数
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, struct flock * lock);
  • 参数说明
参数名称说明
fd文件描述符
cmd操作的命令
lock文件锁数据结构
  • cmd标志位说明
标志位说明
F_GETLK根据lock描述,决定是否上文件锁
F_SETLK设置lock描述的文件锁
  • flock结构体说明
变量类型变量名称变量说明
short intl_type锁定的状态
short intl_whence决定l_start的计算起始位置
off_tl_start锁定区域的开头位置
off_tl_len锁定区域的大小,0表示锁到结尾
pid_tst_nlink锁定动作的进程
  • whence标志位
标志位说明
SEEK_SETl_start为相对文件开始处的值
SEEK_CURl_start为相对当前位置处的值
SEEK_ENDl_start为相对文件结尾处的值

l_len表示加锁的长度,0为到文件末尾,l_pid表示操作文件的进程ID号。

  • 示例
#include <iostream>
#include <cstring>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>

using namespace std;
int main() {
    struct flock lock{};
    int fd = open("test_data.txt", O_RDWR);
    if (fd == -1) {
        return fd;
    }
    lock.l_type = F_WRLCK;      // 写入锁
    lock.l_whence = SEEK_SET;   // 文件的开始文件
    lock.l_start = 0;           // 相对于 l_whence 偏移量
    lock.l_len = 20;            // 锁20个字符
    lock.l_pid = getpid();
    /* 直接锁定文件 */
    int res = fcntl(fd, F_SETLK, &lock);
    cout << "文件上锁结果 : " << res << endl;

    close(fd);
    return 0;
}

内存映射

文件映射可以将普通文件映射到内存中,可以像访问内存一样对文件进行访问,不必再调用read或者write。

  • 相关函数
#include <sys/mman.h>
void * mmap(void * start, size_t length, int port, int flags, int fd, off_t offset);
  • 参数说明
参数名称说明
start映射区起始地址,通常为0,表示为系统默认
length映射数据长度
port表示映射区保护方式
flags指定映射对象类型,组合值
fd文件描述符
offset被映射数据在文件中的起点
  • port类型说明
标志位说明
PROT_EXEC映射区可被执行
PROT_READ映射区可读取
PROT_WRITE映射区可写入
PROT_NONE映射区不可访问
  • flags类型说明
标志位说明
MAP_FIXED固定映射地址,与start配套使用,一般不设置
MAP_SHARED共享映射区域,映射区域允许其他进程共享,对区域写入数据会写入文件中
MAP_RIVATE执行写入操作时会创建副本,不影响源文件
MAP_ANONYMOUS匿名映射,不与文件关联,不与进程共享
MAP_DENYWRITE禁止写入
MAP_LOCKED映射区锁定
  • 示例
#include <iostream>
#include <cstring>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>

using namespace std;
int main() {
    char * mapped_mem, *p;

    // test_data.txt 
    // hello seek file code
	// hello lock file code
    int fd = open("test_data.txt", O_RDWR);
    int length = lseek(fd, 1, SEEK_END);
    lseek(fd, 0, SEEK_SET);
    mapped_mem = (char*) mmap(nullptr, length, PROT_WRITE | PROT_READ, MAP_PRIVATE, fd, 0);

    printf("%s\n", mapped_mem);

    /* 替换所以 hello -> write */
    char * hello_pos = nullptr;
    while ((hello_pos = strstr(mapped_mem, "hello"))) {
        memcpy(hello_pos, "write", 5);
        hello_pos += 5;
    }

    printf("%s\n", mapped_mem);

    close(fd);
    munmap(mapped_mem, length);

    return 0;
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值