Linux C語言實現ls -l
Linux下C語言實現ls -l功能
宗旨:技術的學習是有限的,分享的精神是無限的。需求:用ls -l顯示資料夾下所有的檔案及屬性
分析:1 用ls顯示資料夾下的所有檔案,首先用opendir開啟資料夾,再用readdir讀取資料夾裡面的檔案,最後closedir關閉資料夾。
2 用ls-l比較複雜,打印出來的內容包括:(檔案屬性+檔名)由以下幾部分構成——檔案的型別(d——目錄檔案 -——文字檔案...)檔案的可執行許可權 所屬組的許可權,其它組的許可權 硬連結數檔案擁有者檔案 擁有者組 檔案大小 時間 檔名
解決方案:
1. 要想實現ls -l就得知道通過什麼系統呼叫獲取檔案資訊,首先自然是要通過man來查詢相關的系統呼叫。
man -k file | grep status
man -k file | grep information
man -k file | grep info
通過上面的搜尋就可以得到stat這個系統呼叫獲取檔案屬性。
man 2 stat獲取系統呼叫的詳細使用方法:
#include <sys/types.h> #include <sys/stat.h> #include <unistd.h> struct stat { dev_t st_dev; /* ID of device containing file */ 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 file system I/O */ blkcnt_t st_blocks; /* number of 512B blocks allocated */ time_t st_atime; /* time of last access */ time_t st_mtime; /* time of last modification */ time_t st_ctime; /* time of last status change */ };
只要通過相應欄位進行格式處理就行啦!
2.許可權處理:st_mode就是檔案的許可權部分。St_mode本身就是一個16位的二進位制,前四位是檔案的型別,緊接著三位是特殊許可權,左後九位就是ls -l列出來的九個許可權。
linux本身提供了很多測試巨集來測試檔案的型別的
#define __S_IFMT 0170000 /* These bitsdetermine file type. */ /* File types. */ #define __S_IFDIR 0040000 /*目錄 */ #define __S_IFCHR 0020000 /* 字元裝置 */ #define __S_IFBLK 0060000 /* 塊裝置. */ #define __S_IFREG 0100000 /* 普通檔案. */ #define __S_IFIFO 0010000 /* 管道檔案 */ #define __S_IFLNK 0120000 /* 連結檔案 */ #define __S_IFSOCK 0140000 /* 套接字*/
利用上面的測試巨集就可以判斷檔案的型別,至於檔案的許可權部分可以使用掩碼的方式來處理。
具體程式碼如下:vim file_mode.c
#include"list.h"
//man2 stat可看到檔案基本屬性
void file_mode(struct stat *buf)
{
int i;
char buff[10] = {"----------"};
switch(buf->st_mode & S_IFMT)//按位&獲取檔案基本屬性
{
case S_IFIFO:
buff[0] = 'f';
break;
case S_IFDIR:
buff[0] = 'd';
break;
case S_IFSOCK:
buff[0] = 's';
break;
case S_IFBLK:
buff[0] = 'b';
break;
case S_IFLNK:
buff[0] = 'l';
break;
}
if(buf->st_mode & S_IRUSR )
{
buff[1] = 'r';
}
if(buf->st_mode & S_IWUSR )
{
buff[2] = 'w';
}
if(buf->st_mode & S_IXUSR )
{
buff[3] = 'x';
}
if(buf->st_mode & S_IRGRP )
{
buff[4] = 'r';
}
if(buf->st_mode & S_IWGRP )
{
buff[5] = 'w';
}
if(buf->st_mode & S_IXGRP )
{
buff[6] = 'x';
}
if(buf->st_mode & S_IROTH )
{
buff[7] = 'r';
}
if(buf->st_mode & S_IWOTH )
{
buff[8] = 'w';
}
if(buf->st_mode & S_IXOTH )
{
buff[9] = 'x';
}
for(i = 0; i < 10; i++)
{
printf("%c", buff[i]); //迴圈列印
}
}<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
3.ctime時間處理:stat中的st_mtime是一個時間戳,而ls -l顯示出來的是一個格式化後的字串,所以需要對st_mtime欄位進行格式化處理。
ctime接受一個time_t型別的值,可將其轉換為FriJul 18 22:12:43 2014這樣的格式,其中最前面的星期幾不是我們要的,只要其後的值。程式碼如下:
printf("\t%.12s",4 + ctime(&buf.st_mtime));//buf是一個檔案的stat結構體。
4.檔案所有者和檔案所有者組:通過stat結構體的註釋資訊可以看出,stat只能獲取檔案的uid和gid並不是uid gid對應的使用者或組的名稱。
uid找到對應的使用者名稱是可以通過getpwuid這個系統呼叫獲取,這個系統呼叫接受一個uid返回一個passwd的結構體,這個結構體成員如下:
char *pw_name;//User’s login name.
uid_t pw_uid; //umerical user ID.
gid_t pw_gid; //Numerical group ID.
char *pw_dir;//Initial working directory.
char *pw_shell;//Program to use as shell.<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
gid
對應的組名同樣可以找到一個系統呼叫來獲取,通過
man
我發現可以通過
getgrgid()
系統呼叫傳入一個
gid
返回這個
gid
對應的一個
group
結構體,這個結構體成員如下
:
char *gr_name; /* The name of the group.*/
gid_t gr_gid; /*Numerical group ID.*/
char **gr_mem; /*Pointer to a null-terminated array of character pointers to member names.*/
實現程式碼:
#include "list.h"
void file_gid_uid(int uid, int gid)
{
struct passwd *ptr;
struct group *str; //結構體中存放檔案所有者名和檔案所有者組名
ptr = getpwuid(uid); //呼叫函式獲取檔案所有者指標
str = getgrgid(gid); //呼叫函式獲取檔案所有者組指標
printf("\t%s\t%s", ptr-> pw_name, str -> gr_name); //列印檔案所有者和檔案所有者組
}
5.完整程式碼展示:
// list.h標頭檔案,介面的宣告
#ifndef LIST_H
#define LIST_H
#include<stdio.h>
#include<sys/types.h>
#include<dirent.h>
#include<sys/stat.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
#include<time.h>
#include<grp.h>
#include<pwd.h>
void file_mode(struct stat* buf);
void file_gid_uid(int uid, int gid);
void file_operation(char **argv);
#endif /* LIST_H */<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
// list.c主框架
#include"list.h"
int main(int argc, char **argv)
{
if(argc == 1)
{
argv[1] = "./"; // 預設為當前目錄
}
file_operation(argv);
return 0;
}<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
// file_mode.c檔案屬性
#include"list.h"
//man 2 stat可看到檔案基本屬性
void file_mode(struct stat *buf)
{
int i;
char buff[10] = {"----------"};
switch(buf->st_mode & S_IFMT)//按位&獲取檔案基本屬性
{
case S_IFIFO:
buff[0] = 'f';
break;
case S_IFDIR:
buff[0] = 'd';
break;
case S_IFSOCK:
buff[0] = 's';
break;
case S_IFBLK:
buff[0] = 'b';
break;
case S_IFLNK:
buff[0] = 'l';
break;
}
if(buf->st_mode & S_IRUSR )
{
buff[1] = 'r';
}
if(buf->st_mode & S_IWUSR )
{
buff[2] = 'w';
}
if(buf->st_mode & S_IXUSR )
{
buff[3] = 'x';
}
if(buf->st_mode & S_IRGRP )
{
buff[4] = 'r';
}
if(buf->st_mode & S_IWGRP )
{
buff[5] = 'w';
}
if(buf->st_mode & S_IXGRP )
{
buff[6] = 'x';
}
if(buf->st_mode & S_IROTH )
{
buff[7] = 'r';
}
if(buf->st_mode & S_IWOTH )
{
buff[8] = 'w';
}
if(buf->st_mode & S_IXOTH )
{
buff[9] = 'x';
}
for(i = 0; i < 10; i++)
{
printf("%c", buff[i]); //迴圈列印
}
}
// file_gid_uid.c
#include"list.h"
void file_gid_uid(int uid, int gid)
{
struct passwd *ptr;
struct group *str;//結構體中存放檔案所有者名和檔案所有者組名
ptr = getpwuid(uid);//呼叫函式獲取檔案所有者指標
str = getgrgid(gid);//呼叫函式獲取檔案所有者組指標
printf("\t%s\t%s", ptr ->pw_name, str -> gr_name); //列印檔案所有者和檔案所有者組
}<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
// file_operation.c
#include"list.h"
void file_operation(char** argv)
{
DIR* fd; //定義資料夾型別
struct dirent* fp;//fp資料夾返回值
struct stat buf;
int ret;//獲取檔案屬性
char temp[100]; //中間變數存放檔案路徑
if((fd = opendir(argv[1])) == NULL)//開啟資料夾
{
perror("open file fail!");
exit(0);
}
while((fp = readdir(fd)) != NULL)//迴圈讀取資料夾中的檔案資訊
{
strcpy(temp, argv[1]); //將路徑付給中間變數temp
strcat(temp, fp -> d_name); //把檔名字新增到路徑後面形成完整的路徑
if((ret = stat(temp, &buf)) == -1) //獲取檔案基本屬性
{
perror("stat");
exit(0);
}
file_mode(&buf);//檔案屬性(目錄,連結……,可讀,可寫,可執行)
printf(" %d", buf.st_nlink); //列印連結數
file_gid_uid(buf.st_uid, buf.st_gid); //呼叫函式打印出檔案擁有者和檔案所有者組
printf("\t%ld", buf.st_size); //列印檔案大小
//Time();//獲取時間
printf("\t%.12s ", 4 + ctime(&buf.st_mtime));
printf(" %s\n", fp -> d_name); //列印檔名
}
closedir(fd);
}
#makefile:
######################################
# 可執行檔案和動態連結庫的makefile
######################################
#source file
#原始檔,自動找所有.c和.cpp檔案,並將目標定義為同名.o檔案
SOURCE := $(wildcard *.c) $(wildcard *.cpp)
OBJS := $(patsubst %.c,%.o,$(patsubst%.cpp,%.o,$(SOURCE)))
#target youcan change test to what you want
#目標檔名,輸入任意你想要的執行檔名
TARGET := app
TARGET_2 := libtest.so
#compile andlib parameter
#編譯引數
CC :=gcc
LIBS :=
LDFLAGS :=
DEFINES :=
INCLUDE := -I.
CFLAGS := -g $(DEFINES) $(INCLUDE)#-Wall -O3 -DDEBUG
CXXFLAGS:=$(CFLAGS) -DHAVE_CONFIG_H
SHARE := -fPIC -shared -o
#i think youshould do anything here
#下面的基本上不需要做任何改動了
.PHONY :everything objs clean veryclean rebuild
everything :$(TARGET) $(TARGET_2)
all :$(TARGET) $(TARGET_2)
objs : $(OBJS)
rebuild:veryclean everything
clean :
rm -fr *.o
veryclean :clean
rm -fr $(TARGET)
rm -fr $(TARGET_2)
$(TARGET_2) :$(OBJS)
$(CC) $(CXXFLAGS) $(SHARE) [email protected] $(OBJS)$(LDFLAGS) $(LIBS)
$(TARGET) :$(OBJS)
$(CC) $(CXXFLAGS) -o [email protected] $(OBJS)$(LDFLAGS) $(LIBS)