3. 目录与文件属性(写一个ls命令)
ls找出当前目录文件名,ls -l显示详细信息(文件名字典序排序后)
3.1 文件树
磁盘上的文件和目录被unix组织为一颗目录树
每个节点为目录或文件
目录是特殊的文件,包含许多文件记录
.表示当前目录,..表示上级目录
more,cat等命令可以区分文件和目录,且目录文件包含一定的数据结构
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
DIR *opendir(const char *name);
struct dirent {
ino_t d_ino; /* inode num
ber */
off_t d_off; /* not an offset; see NOTES */
unsigned short d_reclen; /* length of this record */
unsigned char d_type; /* type of file; not supported
by all filesystem types */
char d_name[256]; /* filename */
};
/*
SEE ALSO
open(2), closedir(3), dirfd(3), readdir(3),
rewinddir(3), scandir(3), seekdir(3),
telldir(3)
*/
d_name存储文件名
3.2 编写ls
通过opendir打开目录,返回目录,将其传入readdir,利用循环不断的读出描述文件属性的结构体,打印出其中的d_name
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
#include<dirent.h>
void do_ls(char dirname[])
{
//用于保存指向目录的指针
DIR* dir_ptr;
//用于保存文件信息
struct dirent * direntp;
//打开目录
if((dir_ptr=opendir(dirname))==NULL)
{
fprintf(stderr,"myls:cannot open %s\n",dirname);//打开失败
}
else{
//不断的循环,直到读不到文件属性
while((direntp=readdir(dir_ptr))!=NULL)
{
//输出文件名
printf("%s ",direntp->d_name);
}
closedir(dir_ptr);
}
}
int main(int argc,char* argv[])
{
if(argc==1)
{
do_ls(".");//检索当前目录
}
else{
while(--argc)
{
printf("%s\n",*++argv);
do_ls(*argv);
}
}
return 0;
}
不足:
未排序输出,输出了隐藏文件.
没有选型-l,无法输出文件的详细信息,这些信息不是在目录中存储的
3.3 完善,编写ls -l
ls -l的输出:
提取文件状态信息的函数
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *buf);
//将信息放在buf中,成功返回 0,失败返回-1
struct stat {
dev_t st_dev; /* ID of device containing fi
le */
ino_t st_ino; /* inode number */
mode_t st_mode; /* protection */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device ID (if special file
) */
off_t st_size; /* total size, in bytes */
blksize_t st_blksize; /* blocksize for filesystem I
/O */
blkcnt_t st_blocks; /* number of 512B blocks allo
cated */
/* Since Linux 2.6, the kernel supports nanosecond
precision for the following timestamp fields.
For the details before Linux 2.6, see NOTES. */
struct timespec st_atim; /* time of last access */
struct timespec st_mtim; /* time of last modification */
struct timespec st_ctim; /* time of last status change */
#define st_atime st_atim.tv_sec /* Backward compatibility */
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
};
例如:使用stat获取并输出某文件的大小
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
#include<dirent.h>
#include<sys/stat.h>
int main()
{
struct stat buf;
if(stat("/etc/passwd",&buf)==-1)
{
printf("faled\n");
}
else
{
printf("size:%d\n",buf.st_size);
}
return 0;
}
由于结构体中的user和group字段都是数值,且mode也为数值,所以需要将其转换为响应的字符
模式字段
前4位:文件类型
三位文件特殊属性
九位许可权限:分别对应三组:文件所有者,同组用户和其他用户
每组三位:读,写,执行,1为有,0为无权限
掩码:
将不需要的字段置为0,需要的字段值不发生变化
整数是bit组成的序列
215 = 0 1 1 0 1 0 1 1 1
掩码技术
与0做&操作可以将对应的bit位置为0
使用八进制数
使用掩码得到文件类型
通过使用掩码将其他无关部分置为0,再与文件类型对应的代码比较,从而可以判断文件类型
使用宏简化操作
//使用宏来与目录的mode代码比较(将无关的部分置为0),判断文件类型
#define S_ISFIFO(m) (((m)&(0170000))==(0010000))
#define S_ISDIR(m) (((m)&(0170000))==(0040000))
#define S_ISCHR(m) (((m)&(0170000))==(0020000))
#define S_ISBLK(m) (((m)&(0170000))==(0060000))
#define S_ISREG(m) (((m)&(0170000))==(0100000))
通过解码来获取权限情况
最低的九位在<sys/stat.h>中每一位都有对应的掩码
将用户/组ID转换为字符串
struct stat中的用户和组是以ID形式存在,但是ls要求输出相应的字符串(名字)
/etc/passwd包含了用户列表,所有的用户都对此文件有读权限,但是不一定所有用户多保存在此文件中
还可能保存在NIS中(一台大家都能访问到的主机)
组ID处理
用户要分为不同的组,/etc/group保存所有组信息
passwd文件中的组信息,是用户的主组,用户还可以是其他组的成员
使用
#include <sys/types.h>
#include <grp.h>
struct group *getgrnam(const char *name);
struct group *getgrgid(gid_t gid);
struct group {
char *gr_name; /* group name */
char *gr_passwd; /* group password */
gid_t gr_gid; /* group ID */
char **gr_mem; /* NULL-terminated array of poi
nters
to names of group members */
};
//以下函数可以取得完整的用户列表
#include <sys/types.h>
#include <pwd.h>
struct passwd *getpwnam(const char *name);
struct passwd *getpwuid(uid_t uid);
struct passwd {
char *pw_name; /* username */
char *pw_passwd; /* user password */
uid_t pw_uid; /* user ID */
gid_t pw_gid; /* group ID */
char *pw_gecos; /* user information */
char *pw_dir; /* home directory */
char *pw_shell; /* shell program */
};
如果用户信息保存在etc/passwd,则会去查找passwd,如果在NIS,则会去查找NIS
如果uid不合法,则会返回一个空指针NULL,这样就没意义了,下面在代码实现时会说到如何解决
UID没有对应的用户名的情况
3.4 设置和修改文件的属性
一旦文件创建,则其类型就不能修改
文件类型有:普通文件,设备文件,目录文件等
每个文件都有9位许可权限和3位特殊属性,创建文件后,它们可以被chmod系统调用修改
3.4.1 文件类型
文件一旦创建,类型不可改变,creat创建的是普通文件
3.4.2 许可位,特殊属性位
umask:
#include <sys/types.h>
#include <sys/stat.h>
mode_t umask(mode_t mask);
即使creat请求了文件的许可位,但是内核也不一定会照做,因为内核会通过新建文件掩码来设置:
umask,指定哪些位需要被关掉
chmod系统调用:
#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);
S_ISUID (04000) set-user-ID (set process effective user ID
on execve(2))
S_ISGID (02000) set-group-ID (set process effective group
ID on execve(2); mandatory locking, as
described in fcntl(2); take a new file's
group from parent directory, as described
in chown(2) and mkdir(2))
S_ISVTX (01000) sticky bit (restricted deletion flag, as
described in unlink(2))
S_IRUSR (00400) read by owner
S_IWUSR (00200) write by owner
S_IXUSR (00100) execute/search by owner ("search" applies
for directories, and means that entries
within the directory can be accessed)
S_IRGRP (00040) read by group
S_IWGRP (00020) write by group
S_IXGRP (00010) execute/search by group
S_IROTH (00004) read by others
S_IWOTH (00002) write by others
S_IXOTH (00001) execute/search by others
此调用可以接收八进制,也可以接收sys/stat.h
中定义的值
3.4.3 文件链接数
文件被引用的次数(别名的数量)
3.4.4 文件所有者与组
通过组ID和用户ID来标识文件按所有者和所属的组
#include <unistd.h>
int chown(const char *pathname, uid_t owner, gid_t group);//后两位传入-1则不会改变
文件所有者可以将文件的组改为任何一个他所属的组
文件大小:creat时,可以设置为0,但是不存在能直接减小文件战用空间的函数
3.4.5 文件时间
最后修改时间,最后访问时间,属性最后修改时间
内核会自动修改这三个时间,也可以直接用系统调用来修改:
#include <sys/types.h>
#include <utime.h>
int utime(const char *filename, const struct utimbuf *times);
struct utimbuf {
time_t actime; /* access time */
time_t modtime; /* modification time */
};
可以使用touch命令修改最后访问和最后修改时间
3.4.6 文件名
命令mv改变文件名
//修改文件名或移动文件位置
#include <stdio.h>
int rename(const char *oldpath, const char *newpath);