关于命令ls
问题1:ls命令能做什么
经常用到的命令行选项
问题1的答案
- 如何列出目录的内容
- 如何读取并显示文件属性
- 给出一个名字,要判断出它是目录还是文件
问题2:ls是如何工作
需要注意的
与普通文件不同的是,目录文件永远不会空,每个目录都至少包含两个特殊的项–”.”和”…”
如何读目录的内容
通过man -k direct
找到以下结果
通过阅读readdir
手册,了解到其读取目录中的记录,返回一个指向当前目录项的指针,类型为struct dirent
- Ubuntu 22.04.1 LTS版本中的dirent结构如下:
struct dirent {
ino_t d_ino; /* Inode number */
off_t d_off; /* Not an offset; see below */
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]; /* Null-terminated filename */
};
- 书中提到的SunOS的dirent版本如下:
struct dirent {
ino_t d_ino;
off_t d_off;
unsigned short d_reclen;
char d_name[1];
};
经过查询资料可知,SunOS使用了柔性数组的技术约定,柔性数组允许在结构体的最后一个成员中使用一个长度为 1 的数组来实现可变长度。
例子如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct dirent {
ino_t d_ino;
off_t d_off;
unsigned short d_reclen;
unsigned char d_type;
char d_name[1]; // 柔性数组
};
struct dirent* createDirent(const char* filename) {
size_t filenameLen = strlen(filename);
size_t direntSize = sizeof(struct dirent) + filenameLen; // 计算结构体的总大小
struct dirent* direntPtr = malloc(direntSize);
if (direntPtr == NULL) {
perror("Memory allocation failed");
return NULL;
}
direntPtr->d_ino = 0; // 假设inode为0
direntPtr->d_off = 0; // 假设偏移量为0
direntPtr->d_reclen = direntSize;
direntPtr->d_type = 0; // 假设文件类型为0
strcpy(direntPtr->d_name, filename); // 复制文件名到柔性数组
return direntPtr;
}
int main() {
const char* filename = "example.txt";
struct dirent* direntPtr = createDirent(filename);
if (direntPtr != NULL) {
printf("Inode: %lu\n", direntPtr->d_ino);
printf("Offset: %ld\n", direntPtr->d_off);
printf("Record Length: %hu\n", direntPtr->d_reclen);
printf("File Type: %hhu\n", direntPtr->d_type);
printf("File Name: %s\n", direntPtr->d_name);
free(direntPtr); // 释放动态分配的内存
}
return 0;
}
问题3:如何编写ls
介绍stat
使用man 2 stat
探索struct stat
的成员变量:
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* Inode number */
mode_t st_mode; /* File type and mode */
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; /* Block size for filesystem I/O */
blkcnt_t st_blocks; /* Number of 512B blocks allocated */
// ......
};
下图告诉我们mode字段的具体信息在inode(7)
重点:mode字段
st_mode
是一个16位的二进制数,文件类型的权限被编码在这个数中:
其中前四位用作文件类型,有以下四种:
接下来的三位是文件的特殊属性,目前不是很懂
如何读取编码值呢?
通过阅读inode(7)
可以了解到两种方法:
第一种方式如下:
S_IFMT 0170000 // bit mask for the file type bit field
S_IFSOCK 0140000 // socket
S_IFLNK 0120000 // symbolic link
S_IFREG 0100000 // regular file
S_IFBLK 0060000 // block device
S_IFDIR 0040000 // directory
S_IFCHR 0020000 // character device
S_IFIFO 0010000 // FIFO
1
2
3
4
5
6
// Thus, to test for a regular file (for example), one could write:
stat(pathname, &sb);
if ((sb.st_mode & S_IFMT) == S_IFREG) {
/* Handle regular file */
}
第二种方法如下:
S_ISREG(m) // is it a regular file?
S_ISDIR(m) // directory?
S_ISCHR(m) // character device?
S_ISBLK(m) // block device?
S_ISFIFO(m) // FIFO (named pipe)?
S_ISLNK(m) // symbolic link? (Not in POSIX.1-1996.)
S_ISSOCK(m) // socket? (Not in POSIX.1-1996.)
// The preceding code snippet could thus be rewritten as:
stat(pathname, &sb);
if (S_ISREG(sb.st_mode)) {
/* Handle regular file */
}
如何根据uid和gid得到对应的用户名和组名呢
-
使用
man getpwuid
命令查询相关信息可知:
-
使用
man getgrgid
命令查询相关信息可知:
结果
需要注意的一点是
char dirent_path[100];
if(strcmp(direntp->d_name, ".") != 0 && strcmp(direntp->d_name, "..") != 0){
strcpy(dirent_path, dirname);
if(strcmp(dirent_path, "/") != 0)
strcat(dirent_path, "/");
strcat(dirent_path, direntp->d_name);
}
else{
strcpy(dirent_path, direntp->d_name);
}
DoLs函数中的上一段代码的作用,细想想即可
代码如下:
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
void DoLs(char[]);
void DoStat(char []);
void ShowFileInfo(char *, struct stat *);
void ModeToLetters(int, char[]);
char *UidToName(uid_t);
char *GidToName(git_t);
int main(int argc, char * argv[]){
if(argc == 1)
DoLs(".");
else{
while(--argc){
printf("%s:\n", * ++argv);
DoLs(*argv);
}
}
}
void DoLs(char dirname[]){
DIR *dirptr;
struct dirent *direntp;
if((dirptr = opendir(dirname)) == NULL)
fprintf(stderr, "Myls: cannot open %s\n",dirname);
else{
while((direntp = readdir(dirptr)) != NULL){
char dirent_path[100];
if(strcmp(direntp->d_name, ".") != 0 && strcmp(direntp->d_name, "..") != 0){
strcpy(dirent_path, dirname);
if(strcmp(dirent_path, "/") != 0)
strcat(dirent_path, "/");
strcat(dirent_path, direntp->d_name);
}
else{
strcpy(dirent_path, direntp->d_name);
}
DoStat(dirent_path);
}
closedir(dirptr);
}
}
void DoStat(char *filename){
struct stat info;
if(stat(filename, &info) == -1){
fprintf(stderr,"something is wrong\n");
perror(filename);
}
else
ShowFileInfo(filename, &info);
}
void ShowFileInfo(char *filename, struct stat *info_p){
char mode_str[11];
ModeToLetters(info_p->st_mode,mode_str);
printf("%s", mode_str);
printf("%4d ", (int)info_p->st_nlink);
printf("-8s ", UidToName(info_p->st_uid));
printf("-8s ", GidToName(info_p->st_gid));
printf("%8ld ", (long)info_p->st_size);
char buf[80];
strftime(buf,sizeof(buf),"%Y-%m-%d %H:%M:%S %z",localtime(&info_p->st_mtime));
printf("%s ", buf);
printf("%s\n", filename);
}
void ModeToLetters(int mode, char str[]){
strcpy(str,"----------");
if(S_ISDIR(mode)) str[0] = 'd';
if(S_ISCHR(mode)) str[0] = 'c';
if(S_ISBLK(mode)) str[0] = 'b';
if(mode & S_IRUSR) str[1] = 'r';
if(mode & S_IWUSR) str[2] = 'w';
if(mode & S_IXUSR) str[3] = 'x';
if(mode & S_IRGRP) str[4] = 'r';
if(mode & S_IWGRP) str[5] = 'w';
if(mode & S_IXGRP) str[6] = 'x';
if(mode & S_IROTH) str[7] = 'r';
if(mode & S_IWOTH) str[8] = 'w';
if(mode & S_IXOTH) str[9] = 'x';
}
char *UidToName(uid_t uid){
struct passwd *pw_ptr;
if((pw_ptr = getpwuid(uid)) == NULL){
perror("error:");
}
else
return pw_ptr->pw_name;
}
char *GidToName(gid_t gid){
struct group *grp_ptr;
if((grp_ptr = getgrgid(gid)) == NULL){
perror("error:");
}
else
return grp_ptr->gr_name;
}
运行图
设置和修改文件的属性
file-creation-mask
是一个非常有用的系统变量- 例如要防止程序创建能被同组用户和其他用户修改的文件,可以在程序中执行umask(022);来实现关掉
----w--w-
来实现 - 使用
chmod
来改变文件模式
chmod("/tmp/myfile",04764)
orchmod("tmo/myfile", S_ISUID|S_IRWXU|S_IRGRP|S_IWGRP|S_IROTH)
显然后者具有明显的可读性
目录为什么有x权限
目录的x权限允许用户访问目录