暑假留校前两天
实现ls命令基础功能:
主要问题:
一些玄学问题(改了好久 )
当使用lstat(path,&buf)函数时,如果path路径最后含有 / 符号(例home/),函数无法正常使用。
此处有问题,需要先用一个int数据接一下(图一strcat()下面一行,图二int xxx),不能直接操作。
因为电脑原因,无法完整实现ls -R类全部功能(某些Windows文件无法访问)
- 拓展知识
- 当我在写总用量的时候,在鸟哥里面,我知道了在linux文件系统中,与目录相关的权限和属性等信息是存放在inode中, 目录中的文件名都存放在最小存储单位data block中, 默认为4k也就是4096,但是实际上,并不是所有的文件都能占到4k,所以当目录中的文件容量少于4096时,用ls -l命令就会看到目录的大小是4096,. 当文件的数量大于4096小于8192时, 用ls -l命令就会看到目录的大小是8192, 以此类推。
linux系统中的单个文件的权限和属性等信息是存放在inode中的, 文件的内容是存放在data block中, 跟目录类似, 如果文件的内容大小不足4096个字节, 也会占用一个data block. 因此用ls -l命令查看文件的大小却不是4096的倍数。
所以这就牵扯出,你所看到的单个文件占用容量相加并不是他所占有的有效容量。
// 代码
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <errno.h>
#include <linux/limits.h>
#define PARAM_NONE 0
#define PARAM_A 1
#define PARAM_L 2
#define PARAM_R 4
#define MAXROWLEN 150
int g_leave_len = MAXROWLEN;
int g_maxlen; //存放某目录下最长文件名的长度
int clour; //底色
unsigned long long sum;
void my_err(char error[],int num){
fprintf(stderr,"line :%d",num);
perror(error);
exit(1);
}
void getclour(char name[]){
struct stat buf;
lstat(name,&buf);
if(S_ISREG(buf.st_mode)){
clour = 37;
if((buf.st_mode&S_IXOTH)||(buf.st_mode&S_IXGRP)||(buf.st_mode&S_IXUSR)){
clour = 32;
}
}
else if(S_ISDIR(buf.st_mode)){
clour = 34;
}
else if(S_ISCHR(buf.st_mode)){
clour = 33;
}
else if(S_ISBLK(buf.st_mode)){
clour = 33;
}
else if(S_ISFIFO(buf.st_mode)){
clour = 35;
}
else if(S_ISLNK(buf.st_mode)){
clour = 36;
}
else if(S_ISSOCK(buf.st_mode)){
clour = 36;
}
}
void display_attribute(struct stat buf,char * name)
{
char buf_time[32];
struct passwd *psd;
struct group *grp;
getclour(name);
if(S_ISREG(buf.st_mode)){
printf("-");
}
else if(S_ISDIR(buf.st_mode)){
printf("d");
}
else if(S_ISCHR(buf.st_mode)){
printf("c");
}
else if(S_ISBLK(buf.st_mode)){
printf("b");
}
else if(S_ISFIFO(buf.st_mode)){
printf("f");
}
else if(S_ISLNK(buf.st_mode)){
printf("l");
}
else if(S_ISSOCK(buf.st_mode)){
printf("s");
}
if(buf.st_mode & S_IRUSR){ //用户的权限
printf("r");
}else
printf("-");
if(buf.st_mode & S_IWUSR){
printf("w");
}
else
printf("-");
if(buf.st_mode & S_IXUSR){
printf("x");
}
else
printf("-");
if(buf.st_mode & S_IRGRP){ //用户组的权限
printf("r");
}
else
printf("-");
if(buf.st_mode & S_IWGRP){
printf("w");
}
else
printf("-");
if(buf.st_mode & S_IXGRP){
printf("x");
}
else
printf("-");
if(buf.st_mode & S_IROTH){ //其他用户的权限
printf("r");
}
else
printf("-");
if(buf.st_mode & S_IWOTH){
printf("w");
}
else
printf("-");
if(buf.st_mode & S_IXOTH){
printf("x");
}
else
printf("-");
psd=getpwuid(buf.st_uid);
grp=getgrgid(buf.st_gid);
printf("%4ld ",buf.st_nlink);//打印文件连接数
printf("%-8s ",psd->pw_name);
printf("%-8s",grp->gr_name);
printf("%6ld",buf.st_size);//打印文件大小
sum = sum + buf.st_size;
strcpy(buf_time,ctime(&buf.st_mtime)); //文件的最后修改时间
//ctime函数的作用为把时间转化为字符串
buf_time[strlen(buf_time)-1]='\0'; //去掉换行符
printf(" %s",buf_time);
}
/*在没有使用-l选项时
*/
void display_single(char *name){
int len;
//如果本行不足打印一个文件名换行
if(g_leave_len < g_maxlen){
putchar('\n');
g_leave_len = MAXROWLEN;
}
len = strlen(name);
len = g_maxlen - len;
/* printf("name :%s\n",name); */
printf("\033[%dm%s\033[0m",clour,name);
for(int i = 0;i < len;i++){
printf(" ");
}
printf(" ");
//下面的2指示空两格
g_leave_len -= (g_maxlen + 2);
}
/*根据命令行参数和完整路径显示目标文件
* 参数flag:命令行参数
* 参数pathname:包含文件路径名*/
void display(int flag,char *pathname){
int i,j;
struct stat buf;
char name[10000]; //代表名称的最长值 不同系统可能不同
for(i=0,j=0;i<strlen(pathname);i++)
{
if(pathname[i]=='/') //目录之间的分隔符
{
j=0;
continue;
}
name[j++]=pathname[i];
}
name[j]='\0';
getclour(pathname);
if(lstat(pathname,&buf) == -1){
my_err("stat",__LINE__);
}
switch (flag)
{
case PARAM_NONE:
if(name[0]!='.') //一般情况不显示隐藏文件
display_single(name);
break;
case PARAM_A:
display_single(name);
break;
case PARAM_L:
if(name[0]!='.')
{
display_attribute(buf,name);
printf(" \033[%dm%s\033[0m\n",clour,name);
}
break;
case PARAM_A+PARAM_L:
display_attribute(buf,name);
printf(" \033[%dm%s\033[0m\n",clour,name);
break;
case PARAM_R:
if(name[0]!='.'){
display_single(name);
}
break;
case PARAM_R + PARAM_A:
display_single(name);
break;
case PARAM_L + PARAM_A + PARAM_R:
display_attribute(buf,name);
printf(" \033[%dm%s\033[0m\n",clour,name);
break;
case PARAM_L + PARAM_R:
if(name[0]!='.'){
display_attribute(buf,name);
printf(" \033[%dm%s\033[0m\n",clour,name);
}
break;
default:
break;
}
}
void display_dir(int flag_param,char * path)
{
DIR *dir;
struct dirent *ptr;
int count=0;
char temp[10000];
dir=opendir(path);
if(dir==NULL)
{
my_err("opendir",__LINE__);
}
//统计出最长文件名长度和文件总数
while((ptr=readdir(dir))!=NULL)
{
if(g_maxlen<strlen(ptr->d_name))
{
g_maxlen=strlen(ptr->d_name);
}
count++;
}
char **filename = (char **)malloc(sizeof(char*)*(count+1));
for(int i = 0;i < count+1;i++){
filename[i] = (char *)malloc(sizeof(int)*10000);
}
closedir(dir);
int i,j,len=strlen(path);
//获取该目录下的所有文件名
dir=opendir(path);
for(int i=0;i<count;i++)
{
ptr=readdir(dir);
if(ptr==NULL) my_err("readdir",__LINE__);
strncpy(filename[i],path,len);
filename[i][len]='\0'; //因为strcat的实现需要最后一位是‘\0’
strcat(filename[i],ptr->d_name);
filename[i][len+strlen(ptr->d_name)]='\0';
}
for(i = 0;i < count-1;i++){
for(j = 0;j < count-1-i;j++){
if(strcmp(filename[j],filename[j+1]) > 0){
strcpy(temp,filename[j+1]);
temp[strlen(filename[j+1])] = '\0';
strcpy(filename[j+1],filename[j]);
filename[j+1][strlen(filename[j])] = '\0';
strcpy(filename[j],temp);
filename[j][strlen(temp)] = '\0';
}
}
}
sum = 0;
for(int i = 0;i < count;i++){
getclour(filename[i]);
display(flag_param,filename[i]);
}
if(flag_param & PARAM_L){
printf("总用量:%lld\n",sum);
}
if(flag_param & PARAM_R){
rewinddir(dir);
for(i = 0;i < count-1;i++){
for(j = 0;j < count-1-i;j++){
if(strcmp(filename[j],filename[j+1]) > 0){
strcpy(temp,filename[j+1]);
temp[strlen(filename[j+1])] = '\0';
strcpy(filename[j+1],filename[j]);
filename[j+1][strlen(filename[j])] = '\0';
strcpy(filename[j],temp);
filename[j][strlen(temp)] = '\0';
}
}
}
while((ptr=readdir(dir))!=NULL)
{
char path2[_PC_PATH_MAX+1];
strcpy(path2,path);
path2[strlen(path2)+1]='\0';//保证字符串末尾为空零 方便进行字符串操作
strcat(path2,ptr->d_name);
int x = strlen(path2);
/* path2[x]='/'; */
path2[x]='\0';//保证字符串末尾为空零 方便进行字符串操作
struct stat buf2;
lstat(path2,&buf2);
if(strcmp(ptr->d_name,"..")!=0 && strcmp(ptr->d_name,".")!=0 && S_ISDIR(buf2.st_mode)){
putchar('\n');
char path2[_PC_PATH_MAX+1];
strcpy(path2,path);
path2[strlen(path2)+1]='\0';//保证字符串末尾为空零 方便进行字符串操作
strcat(path2,ptr->d_name);
int x = strlen(path2);
path2[x]='/';
path2[x+1]='\0';//保证字符串末尾为空零 方便进行字符串操作
/* getclour(path2); */
printf("%s\n",path2);
display_dir(flag_param,path2);
}
}
}
closedir(dir);
int xxx = flag_param & PARAM_L;
if(xxx == 0){
putchar('\n');
} //没有l选项的时候打印一个换行符
}
int main(int argc,char **argv){
int i=0,j=0,k=0,num=0;
char path[1000+1];
char param[40];//保存命令行参数
int flag_param=0;//参数种类l、a,r
struct stat buf;
for(i=0;i<argc;i++){
if(argv[i][0]=='-'){
for(k=1;k<strlen(argv[i]);k++,j++) //j是出现的参数总数
param[j]=argv[i][k]; //保存命令行参数
num++; //保存 ’-‘ 的数量
}
}
//只支持少量参数,其他参数无法使用,报错
for(i = 0;i < j;i++){
if(param[i]=='a')
{
flag_param|=PARAM_A;
}
else if(param[i]=='l')
{
flag_param|=PARAM_L;
}
else if(param[i]=='R'){
flag_param|=PARAM_R;
}
else
{
printf("is a invaild param!\n");
exit(1);
}
}
i = 1;
//如果输入没有目录,直接开始当前目录的
if(num+1 == argc){
path[0] = '.';
path[1] = '/';
path[2] = '\0';
/* printf("%s\n",path); */
display_dir(flag_param,path);
return 0;
}
while(i<argc){
/* printf("%d\n",i); */
if(argv[i][0]=='-')
{
i++;
continue;
}
else{
/* printf("i =============== %d\n",i); */
int ii = i;
/* printf("ii============%d\n",ii); */
strcpy(path,argv[i]);
/* printf("ii = %d\n",ii); */
if(stat(path,&buf)==-1)
{
my_err("stat",__LINE__);
}
if(S_ISDIR(buf.st_mode))
{
/* printf("i = %d\n",i); */
/* printf("path:%s\n",path); */
/* printf("ii = %d",ii); */
/* printf("adsa:i%d\n",i); */
/* printf("strlen():%ld\n",strlen(argv[ii])); */
char a = path[strlen(argv[ii])-1];
/* printf("%c\n",a); */
if(path[strlen(argv[ii])-1]!='/')
{
path[strlen(argv[ii])]='/';
path[strlen(argv[ii])+1]='\0';//保证字符串末尾为空零 方便进行字符串操作
}
else{
path[strlen(argv[ii])]='\0';
}
/* printf("path:%s\n",path); */
display_dir(flag_param,path);
}
else{
display(flag_param,path);//参数为一个文件
}
i++;
}
}
return 0;
}
要求在任何目录下可以使用ls命令,我的实现方法
#通过修改.bashrc文件:
vim ~/.bashrc
#在最后一行添上:
export PATH=【目录】:$PATH
#生效方法:(有以下两种)
#1、关闭当前终端窗口,重新打开一个新终端窗口就能生效
#2、输入“source ~/.bashrc”命令,立即生效
#有效期限:永久有效
#用户局限:仅对当前用户
如果仅需要想要短期实现
export PATH=/usr/local/bin:$PATH
#配置完后可以通过echo $PATH查看配置结果。
#生效方法:立即生效
#有效期限:临时改变,只能在当前的终端窗口中有效,当前窗口关闭后就会恢#复原有的path配置
#用户局限:仅对当前用户
- 完成任务
功能实现:
项目 | Value |
---|---|
基础功能 | 基本实现ls -alR,在任何目录下可以使用 |
拓展功能 | 颜色 |