Linux编程实践Day3

关于命令ls

问题1:ls命令能做什么

经常用到的命令行选项

在这里插入图片描述

问题1的答案

在这里插入图片描述

  1. 如何列出目录的内容
  2. 如何读取并显示文件属性
  3. 给出一个名字,要判断出它是目录还是文件

问题2:ls是如何工作

需要注意的

与普通文件不同的是,目录文件永远不会空,每个目录都至少包含两个特殊的项–”.”和”…”
如何读目录的内容
通过man -k direct找到以下结果
在这里插入图片描述

通过阅读readdir手册,了解到其读取目录中的记录,返回一个指向当前目录项的指针,类型为struct dirent

  1. 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 */
};
  1. 书中提到的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;
}
运行图

在这里插入图片描述

设置和修改文件的属性
  1. file-creation-mask是一个非常有用的系统变量
  2. 例如要防止程序创建能被同组用户和其他用户修改的文件,可以在程序中执行umask(022);来实现关掉----w--w-来实现
  3. 使用chmod来改变文件模式
    chmod("/tmp/myfile",04764) or chmod("tmo/myfile", S_ISUID|S_IRWXU|S_IRGRP|S_IWGRP|S_IROTH) 显然后者具有明显的可读性

在这里插入图片描述

目录为什么有x权限

目录的x权限允许用户访问目录

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值