Unix代码实例1的实现及分析

一、ls(1)命令的简要实现。

#include<apue.h>            
#include<dirent.h>
int main(int argc,char *argv[])
{
  DIR *dp;
  struct dirent *dirp;
  
  if(argc!=2)
    err_quit("usage:ls directory_name");

  if((dp=opendir(argv[1]))==NULL)
    err_sys("can't open %s",argv[1]);
  while((dirp=readdir(dp))!=NULL)
    printf("%s\n",dirp->d_name);

  closedir(dp);
  exit(0);  
}
  1. 以上代码实例的作用是列出一个目录中的所有文件;
  2. 以下是编译运行时可能出现的问题;
    我是用ubuntu下的vscode编辑好后用gcc编译再运行的,当然你也 可以在vscode里面的应用商店下载好适合c/c++的扩展,然后Run code运行,当遇到vscode不能输入的问题时可以在Code Runner扩展中的Run In Teiminal项打勾✔。
  3. 以下是运行过程及结果;
    编译过程我们都知道有四步骤但是我们可以简化为一步:
    gcc xxx.c(xxx.cpp) -o xxx 其中xxx.c/xxx.cpp是c或cpp文件 -o 是将前面的c或cpp文件输出成指定名称的可执行文件,这里的可执行文件是xxx。执行文件时的操作: ./xxx / (最后的/代表我想列出的所有文件的那个目录 这里是根目录/)
liufangzhou@liufangzhou-virtual-machine:~/桌面/test$ ls
1      homework1    ls1           ls的简单实现.cpp  test
作业1  homework1.c  ls的简单实现  myls              test.cpp
liufangzhou@liufangzhou-virtual-machine:~/桌面/test$ gcc ls的简单实现.cpp -o ls1
liufangzhou@liufangzhou-virtual-machine:~/桌面/test$ ./ls1 /
media	usr	sys	run	lib32	var	lost+found	boot	proc	..	bin	srv	etc	.	root	home	sbin	swapfile	lib	tmp	libx32	lib64	mnt	opt

输出格式在书中是\n但是那样显示时太长了,我在这改为了\t。
二、对实例的分析

  1. 首先是头文件,如果你刚买来书不久想试运行书中代码实例,会发现运行不了,因为没有apue.h这个头文件,因为这个头文件是作者自己写的,那么我们就得通过某些途径找到这个头文件。

    ①通过www.apuebook.com/下载src.3e.tar.gz
    ②通过异步书店网站上的资源界面下载

tar -zcvf src.3e.tar.gz
cd /apue/apue.3e/
make
ls

找到include文件夹,将aupe.h拷贝到c/cpp文件默认的includePath下,在这里我用的vscode的默认include路径是/usr/include/再将apue.3e中lib目录下的error.c文件拷贝到includePath下,打开apue.h文件,在#endlif前加上#include “error.c”

liufangzhou@liufangzhou-virtual-machine:~/桌面/资源/apue.3e/include$ cd /usr/include
liufangzhou@liufangzhou-virtual-machine:/usr/include$ gedit apue.h

这样就可以编译运行实例1了。
2. 在apue.h中添加#include "error.c"的原因
因为在apue.h中只声明了err_quit()和err_sys()这两个函数并没有实现函数功能,实现的函数功能的内容在error.c(也就是.c文件)中,这样可以直接引用apue.h这个头文件,当然也可以直接在编辑器里引用error.c文件。
🔺main函数中 argc 和 *argv[]是只在需要命令行参数时使用,比如前面通过gcc编译输出可执行文件后,执行可执行文件,这时需要传入一个要显示所有文件的目录名,就必须用这种方式。
🔺当我们用集成开发环境,比如Dev-c++和vs 2019等时,可以不通过命令行传入参数,这时main函数就可以无形参。
🔺argc是需要在命令行传入的参数个数(即记录你在命令行上输入的字符串的个数),argv[]是参数字符数组,其中第0个参数是程序本身的名称(包含路径)。
🔺 argv[]是一个指针字符数组.
argv[0]:指向程序的全路径名
argv[1]:指向在命令行中执行程序名后的第一个字符串。
argv[2]:指向第二个字符串。

liufangzhou@liufangzhou-virtual-machine:~/桌面/test$ cd /usr/include
liufangzhou@liufangzhou-virtual-machine:/usr/include$ gedit error.c

通过gedit可以查看到error.c中的函数实现
err_quiterr_sys
error.c中这两个函数的描述:

  1. err_quit函数是出现了与系统调用无关的致命错误时打印打印相关信息并终止程序。
  2. err_sys函数是出现与系统调用有关的致命错误时打印相关信息并终止程序。
if(argc!=2)
    err_quit("usage:ls directory_name");

▲根据实例1程序代码,实例1是对ls命令的简单实现,需要传入的参数数目为2个,argc记录在命令行输入的字符串个数,即./ls1 / (这里./ls1是第一个参数, /是第二个参数)当argc!=2即需要命令行传入的参数个数不为2时执行err_quit函数并打印错误信息,因为我们根据代码实现的功能要执行./ls1这个可执行文件就必须输入这两个参数;

if((dp=opendir(argv[1]))==NULL)
    err_sys("can't open %s",argv[1]);

▲当调用opendir这个函数返回一个空指针时,要么没有这个目录,要么有这个目录但是你输错了。所以这时就得用err_sys这个函数打印在系统调用出错时的信息。

while((dirp=readdir(dp))!=NULL)
  printf("%s\n",dirp->d_name);

▲循环调用readdir函数读取每一个目录项并打印目录项的目录名,当没有目录项可读时返回一个空指针,结束while循环。

closedir(dp);
exit(0);  

▲closedir是关闭参数dp所指的目录流,关闭成功返回0,关闭失败返回1。
程序结束时以参数为0调用exit函数终止程序。一般来说,0是正常结束,1~255是出错。

typedef struct __dirstream DIR;

▲dirent.h中对DIR的定义。

struct __dirstream
{
void *__fd; /* `struct hurd_fd' pointer for descriptor.   */
char *__data; /* Directory block.   */
int __entry_data; /* Entry number `__data' corresponds to.   */
char *__ptr; /* Current pointer into the block.   */
int __entry_ptr; /* Entry number `__ptr' corresponds to.   */
size_t __allocation; /* Space allocated for the block.   */
size_t __size; /* Total valid data in the block.   */
__libc_lock_define (, __lock) /* Mutex lock for this structure.   */
};

▲结构体DIR成员项。
DIR结构体类似于FILE,是一个内部结构,用这个内部结构保存当前正在被读取的目录的有关信息

DIR *dp;
struct dirent *dirp;

我们使用的DIR结构也只是声明了该结构的指针,那么DIR结构应该也是同FILE结构一样,在打开一个目录的时候,由内核帮我们分配该结构体的内存。
dirent结构也是如此。

struct dirent
{
   long d_ino; /* inode number 索引节点号 */
   off_t d_off; /* offset to this dirent 在目录文件中的偏移 */
   unsigned short d_reclen; /* length of this d_name 文件名长 */
   unsigned char d_type; /* the type of d_name 文件类型 */
   char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */
}

关于dirent结构体,首先我们要弄清楚目录文件(directory file)的概念:这种文件包含了其他文件的名字以及指向与这些文件有关的信息的指针,从定义能够看出,dirent不仅仅指向目录,还指向目录中的具体文件,readdir函数同样也读取目录下的文件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值