【linux】:linux下文件的使用以及文件描述符(上)

简介: 【linux】:linux下文件的使用以及文件描述符(上)

前言



理解文件原理和操作:

我们先快速回忆下一C语言的文件操作:

4b9bc1ffdc624addb785792f86abc3c1.png


首先看一下fopen函数的使用,然后我们写一段简单的C语言打开文件的代码如下图:

#include <stdio.h>
#define LOG "log.txt"
int main()
{
  //默认写方式打开文件,如果文件不存在,就删除它
  FILE* fp = fopen(LOG,"w");
  if (fp==NULL)
  {
    perror("fopen:");
    return 1;
  }
  //正常进行文件操作
  const char* msg = "hello friends\n";
  int cnt = 1;
  while (cnt)
  {
    fputs(msg,fp);
    --cnt;
  }
  fclose(fp);
  return 0;
}


接下来我们运行一下:

050b5363cb0946739adca264abd63702.png

通过上图我们可以看到成功将我们的字符串写入文件中,那么这次我们什么都不写再看看:

fdf2d420d4ca4888b00b90c5d7a6607c.pngf71a0ec33b994ff58bd9cb0ddc78d4a8.png


为什么原先log里的字符串没了呢?因为我们打开的方式是w,文件内容会先被清空再写入。

下面我们再介绍一个接口snprintf:

2b6de12b9b544b71a13fef0743c45ad8.png


普通的printf是默认向显示器打印,fprintf是指定文件流向指定文件进行打印,下面我们演示一下:

首先我们修改一下代码,将fputs替换为fprintf函数

int main()
{
  //默认写方式打开文件,如果文件不存在,就删除它
  FILE* fp = fopen(LOG,"w");
  if (fp==NULL)
  {
    perror("fopen:");
    return 1;
  }
 //正常进行文件操作
  const char* msg = "hello friends\n";
  int cnt = 5;
  while (cnt)
  {
   // fputs(msg,fp);
    fprintf(fp,"%s:%d:sxy\n",msg,cnt);
    --cnt;
  }
  fclose(fp);
  return 0;
}


ac8e727dc6814dbca0f0f0c8fdbc22b7.png

运行结果如上图所示,并且我们不仅可以像文件中打印,也可以在输出流中打印:

14126e135e0a4a7ba4270da5e628e96a.png


我们都知道,一个程序会默认打开以上三个输入输出流,下面我们像stdout打印一下:ae9c660e6b3f4b399410842b31deb5b7.png93f26cbf0ca149c298296e0c1d2f1c2c.png


运行后我们发现直接就能显示不用再打开log.txt查看了,下面我们再来演示一下snprintf函数:

int main()
{
  //默认写方式打开文件,如果文件不存在,就删除它
  FILE* fp = fopen(LOG,"w");
  if (fp==NULL)
  {
    perror("fopen:");
    return 1;
  }
 //正常进行文件操作
  const char* msg = "hello friends\n";
  int cnt = 5;
  while (cnt)
  {
    char buffer[256];
    snprintf(buffer,sizeof(buffer),"%s:%d:sxy\n",msg,cnt);
    fputs(buffer,fp);
    --cnt;
  }
  fclose(fp);
  return 0;
}



snprintf就是将msg格式化到buffer数组里并且以buffer的大小进行打印,然后将数组中的数据写入文件中。

2d6981692cb7420088ce6b6c2bbd068c.png


后面还有a追加写入等的方式我们就不在演示,这些都是我们C语言的时候学过的。

以上都是语言进行文件的操作,下面我们进入今天的正题,在系统层面使用文件操作。


一、linux系统中的文件操作以及文件接口



首先我们看一下系统中文件的接口open:


8dbfb80a3e1c49f0982e2f01fce296cb.png


然后我们再看一下这个接口的返回值是什么:

3abe030b766049a589af46f8bb9dd6e0.png


如果成功返回一个新的文件描述符,否则返回-1,下面我们演示一下这个接口:

首先一定要包头文件:

f6f45daaff7f4253bd2d00edece224fe.png


这个函数的第一个参数与刚刚一样都是文件名,第二个参数其实是用位图实现的,就是用一个标志位充当不同的行为,如下图所示:

71dc49ee99a047e8bdb2933b6ed74240.png

在上图中我们就利用位图传不同的flags就能打打印不同的数,并且不仅可以打印一个也可以同时打印多个:

11e7d9104cd04cacbb49edd82e6e8aad.png9cd8b53f8612417e96b7ca5667a85e8e.png


而第二个参数的使用方式与上面演示的一样,都有下面这几种选项:


ded46c57d792467c9c22c1957b0b86bf.png

这些都是宏像我们刚刚演示的#define的那样。下面我们演示这个接口:

//系统层面
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define LOG "log.txt"
int main()
{
  int fd = open(LOG,O_WRONLY);
  printf("fd:%d\n",fd);
  close(fd);
  return 0;
}


0_CREAT是如果没有这个文件就给我们创建一个文件,O_WRONLY是只写的意思。

26eed91ff2a043a1959127329ff92c40.png

我们先删除原先的log文件,运行后发现返回值为-1,也就是说出错了,那是什么原因呢?我们修改一下代码如果出错就打印一下出错原因:

e82987679bca408dbfde187b3bac2f22.png23c4470e521e47c4872fb4fcb2f98c8b.png

因为我们要写这个文件,但是这个文件并不存在,所以我们没有的话应该先创建一个文件:

f9d24ccf8c94494d9440a3ea6dade890.png19d6d81111b44e0bba71a773abfdaddb.png


这次我们发现文件创建成功了,错误码为0。我们这里用的是root所以文件的权限没有问题,如果这里是普通用户创建文件的话文件权限是乱码有问题的,如下图:

54c0640266414b69b5971c6241213e45.png


所以我们一般创建文件用的是三个接口的那个函数,我们演示的这个函数没有提供文件权限的接口。

836cf421c1c744d9966bdafe148f1655.png

下面我们用三个接口的open创建一下文件:

d4a86f29b9e74edfbc2abab990c7ceb1.png63484de293c84158850e4218d952f320.png


我们先删掉之前的文件,然后重新运行一下发现这次文件的权限没有问题是正确的(不是666是因为受umask影响)。所以,一个文件被创建默认权限受umask影响,那么如果想不受umask影响呢?

b200727d86834cedbaafafcbbe8f0703.png


我们可以直接将所有的权限掩码设置为0:

f23d58fa140a4737872ee28e42e13343.png8c6d6e03bf2f431aae700f7b8f38432d.png


我们发现这次就不受umask影响了。接下来我们写入文件试试:

关于写入的文件接口的函数为write:

2e5a4e10ee7b4c5e8af7a8d75fee4944.png


向文件描述符写入一个缓冲区大小为count,返回值是实际写入多少字节,写入失败返回-1.

int main()
{
  umask(0);
  int fd = open(LOG,O_WRONLY | O_CREAT,0666);
  if (fd==-1)
  {
    printf("fd:%d,errno:%d,errstring:%s\n",fd,errno,strerror(errno));
  }
  else printf("fd:%d,errno:%d,errstring:%s\n",fd,errno,strerror(errno));
  printf("fd:%d\n",fd);
  const char* msg = "hello friend";
  int cnt = 5;
  while (cnt)
  {
    char line[128];
    snprintf(line,sizeof(line),"%s,%d\n",msg,cnt);
    write(fd,line,strlen(line));
    --cnt;
  }
  close(fd);
  return 0;
}


接下来我们运行一下:

ac5e4beaed3540afbb8a1bc69049302d.png


通过上图我们发现写入成功,其实在我们刚刚strlen的时候发现我们并没有带\0,为什么没有\0也能成功写入呢,因为\0是C语言的标准不是系统的规定,并且\0在系统中写入会变成乱码。


下面我们重新写一下:

7f10b275c7ef477bad949c4a9f737270.pnged5e1f7c5d66420b99d5e6250c48eb19.png


在我们重新运行后发现文件中保留了上一次的数据,也就是说O_CREAT | O_WRONLY是不会清空原来的文件再写入的,这个时候我们加上O_TRUNC选项就可以每次写入前先清空文件了:

1c4eb69d6df44093b2b4b60f2eead12f.png791fa3dc96a54d3fb7fd60fd2f00e5c9.png


搞定了写入那么饿追加就不是问题了,追加只需要注意将写入替换为追加,并且我们不希望每次打开文件先清空文件,所以不需要O_TRUNC选项:

f8e39f73532d4533b43a35eb79d7fde4.png22efbf0b454b4973b77e181f6f253aea.png

为什么我们上面的运行结果不正确呢?因为0_APPEND只是追加不是追加写,所以才没有写入。下面我们修改一下代码:

74ede980ace7475da6d2eafba70c8683.pngf39e110369584cb3ae869902b4a276ca.png

这样我们就成功的追加了字符串,那么如果是只读呢?其实只读就很简单了,只读只需要调用两个接口的open函数即可,因为只读默认认为是有文件的。

9bbef5925f90420b9ed3b558a6f643e5.png


读取的返回值就是当前读取了多少字节,读取到文件结尾就是0,失败就是-1。

int main()
{
  umask(0);
  int fd = open(LOG,O_RDONLY);
  if (fd==-1)
  {
    printf("fd:%d,errno:%d,errstring:%s\n",fd,errno,strerror(errno));
  }
  else printf("fd:%d,errno:%d,errstring:%s\n",fd,errno,strerror(errno));
  char buffer[1024];
  ssize_t n = read(fd,buffer,sizeof(buffer)-1); //使用系统接口来进行IO的时候,一定要注意\0问题。
  if (n>0)
  {
    buffer[n] = '\0';
    printf("%s\n",buffer);
  }
  close(fd);
  return 0;
}


c04ac4cc81d243aabc25cf2e6f80fa10.png


我们在读取的时候为什么-1呢?因为我们不考虑\0的问题,只有当读取的时候返回值大于0我们将\0放到n的位置以字符串的形式打印的时候才需要\0,所以最后需要将\0放到n的位置再打印。

以上就是系统级的文件接口,而入C语言等要进行文件操作都必须调用系统接口。


目录
相关文章
|
16天前
|
运维 安全 Linux
Linux中传输文件文件夹的10个scp命令
【10月更文挑战第18天】本文详细介绍了10种利用scp命令在Linux系统中进行文件传输的方法,涵盖基础文件传输、使用密钥认证、复制整个目录、从远程主机复制文件、同时传输多个文件和目录、保持文件权限、跨多台远程主机传输、指定端口及显示传输进度等场景,旨在帮助用户在不同情况下高效安全地完成文件传输任务。
112 5
|
16天前
|
Linux Shell 数据库
Linux文件查找新姿势:总有一种你没见过
【10月更文挑战第18天】文件查找是Linux用户提升工作效率的重要技能。本文介绍了几种实用的文件查找方法,包括基础的`find`命令、快速的`locate`和`mlocate`、高效的`fd`工具、以及结合`grep`和`rg`进行内容搜索。此外,还提供了编写Shell脚本和使用图形界面工具的建议,帮助你更灵活地管理文件。
54 3
|
4天前
|
网络协议 Linux
linux系统重要文件目录
本文介绍了Linux系统中的重要目录及其历史背景,包括根目录、/usr、/etc、/var/log和/proc等目录的结构和功能。其中,/etc目录下包含了许多关键配置文件,如网卡配置、DNS解析、主机名设置等。文章还详细解释了各目录和文件的作用,帮助读者更好地理解和管理Linux系统。
18 2
|
3天前
|
缓存 监控 Linux
|
27天前
|
Linux 开发工具 数据安全/隐私保护
linux异常一:feng 不在 sudoers 文件中,此事将被报告。yum提示Another app is currently holding the yum lock; waiting for
这篇文章介绍了在CentOS 7系统中安装Docker时遇到的两个常见问题及其解决方法:用户不在sudoers文件中导致权限不足,以及yum被锁定的问题。
36 2
linux异常一:feng 不在 sudoers 文件中,此事将被报告。yum提示Another app is currently holding the yum lock; waiting for
|
7天前
|
Linux Shell 数据库
文件查找是Linux用户日常工作的重要技能介绍了几种不常见的文件查找方法
文件查找是Linux用户日常工作的重要技能。本文介绍了几种不常见的文件查找方法,包括使用`find`和`column`组合、`locate`和`mlocate`快速查找、编写Shell脚本、使用现代工具`fd`、结合`grep`搜索文件内容,以及图形界面工具如`Gnome Search Tool`和`Albert`。这些方法能显著提升文件查找的效率和准确性。
25 2
|
10天前
|
Linux 数据库
linux 全局搜索文件
在 Linux 系统中,全局搜索文件常用 `find`、`locate` 和 `grep` 命令。`find` 根据文件名、类型、大小、时间戳等条件搜索;`locate` 通过预构建的数据库快速查找文件;`grep` 在文件中搜索特定文本,常与 `find` 结合使用。选择合适的命令取决于具体需求。
|
14天前
|
Linux 开发工具 Perl
Linux命令替换目录下所有文件里有"\n"的字符为""如何操作?
【10月更文挑战第20天】Linux命令替换目录下所有文件里有"\n"的字符为""如何操作?
30 4
|
13天前
|
运维 安全 Linux
Linux文件清空的五种方法总结分享
每种方法各有优势,选择最合适的一种或几种,可以极大提高您的工作效率。更多有关Linux系统管理的技巧与资源,欢迎访问,持续提升您的运维技能。
59 1
|
23天前
|
Linux Shell 数据库
Linux文件查找新姿势:总有一种你没见过
文件查找是Linux用户提升工作效率的关键技能。本文介绍了几种不常见的文件查找方法,包括使用`find`结合`column`美化输出、利用`locate`和`mlocate`快速查找、编写Shell脚本自动化任务、使用现代工具`fd`以及结合`grep`和`rg`进行内容搜索。此外,还推荐了几款图形界面搜索工具。掌握这些技巧,让你的文件查找更加高效便捷。
47 2