1. 程式人生 > >Linux C語言實現ls -l

Linux C語言實現ls -l

LinuxC語言實現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只能獲取檔案的uidgid並不是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)