【Linux】Linux仿写bash程序(二)

一、实现操作命令

上一个我们只是简单的写了bash程序,但是如果要用bash实现一些自己实现的命令,比如pwd、ls等命令。
仿写bash程序
将这些文件存放在一个文件夹中,在bash中调用即可。

二、pwd实现

int main()
{
	char path[128] = { 0 };
	getcwd(path,127);
	printf("%s\n",path);
	exit(0);
}

在bash.c 的外置命令中将路经改成pwd所在路经即可。
如:

三、ls 实现

1、ls 为默认显示当前共工作路径下的文件

它的操作形式为:
ls 路经 ==》显示指定路径下的文件
ls -a ==》显示所有文件(包括隐藏文件)
ls -i ==》输出文件i结点的索引信息
ls -l ==》列出文件的详细信息

2、strstr(str1,str2) 函数用于判断字符串str2是否是str1的子串

  • 如果是,则该函数返回 str1字符串从 str2第一次出现的位置开始到 str1结尾的字符串;否则,返回NULL。
  • 原型:string strstr( string1,string2)

3、opendir函数 打开一个目录,在失败的时候返回一个空的指针。

  • 头文件
#include<sys/types.h>
#include<dirent.h>
  • 函数原型
DIR* opendir (const char * path );

4、readdir函数 读一个目录

  • 头文件
#include<dirent.h>
  • 用法
struct dirent * readdir(DIR * dir);
  • readdir()返回参数dir 目录流的下个目录进入点。结构dirent 定义如下:
struct dirent
{
	ino_t d_ino;  //inode number 索引节点号
    off_t d_off; //not an offset; see NOTES 在目录文件中的偏移
    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]; /* filename 文件名,最长255字符*/
};
  • 每次使用readdir后,readdir会读到下一个文件,readdir是依次读出目录中的所有文件,每次只能读一个

5、_stat函数用来获取指定路径的文件或者文件夹的信息。

  • 头文件
#include <sys/types.h>    
#include <sys/stat.h>  
  • struct stat 结构体详解:
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   最后一次改变该文件状态的时间   
};
  • stat结构体中的st_mode ,定义了多种情况,在这里我只简单地说几种:
	S_IRUSR(S_IREAD) 00400     文件所有者具可读取权限
    S_IWUSR(S_IWRITE)00200     文件所有者具可写入权限
    S_IXUSR(S_IEXEC) 00100     文件所有者具可执行权限
    
	S_IRGRP 00040             用户组具可读取权限
    S_IWGRP 00020             用户组具可写入权限
    S_IXGRP 00010             用户组具可执行权限
​
    S_IROTH 00004             其他用户具可读取权限
    S_IWOTH 00002             其他用户具可写入权限
    S_IXOTH 00001             其他用户具可执行权限

	S_ISLNK (st_mode)    判断是否为符号连接
    S_ISREG (st_mode)    是否为一般文件
    S_ISDIR (st_mode)    是否为目录

6、Linux下输出彩色字符

  • 举例:
printf("\033[1;40;32m%s\033[0m",” Hello,NSFocus\n”);

含义:

  • \033 声明了转义序列的开始
  • [1 定义了高亮显示字符;背景颜色(40表示黑色背景);前景颜色(32表示绿色) m%s
  • \033[0m 关闭转义序列, \033[0m 是终端默认颜色
前景背景颜色
3040黑色
3141紅色
3242綠色
3343黃色
3444藍色
3545紫紅色
3646青藍色
3747白色
代码意义
0终端默认设置
1高亮显示
4使用下划线
7反白显示
8不可见

7、代码

注:输出文件的详细信息只写了一个框架,也就是ls -l 还不能实现

//ls.c
#include<sys/types.h>
#include<dirent.h>
#include<sys/stat.h>

#define OPTION_A 0
#define OPTION_I 1
#define OPTION_L 2

#define SETOPTION(option,val) (option)|=(1<<(val))//将option或等1后左移val位

#define ISSET(option,val) (option)&(1<<(val))//判断值到底是哪一个选项

int option = 0;//按位存储相关的选项
//0->a;1->i;2->l
void GetOption(char *argv[], int argc)//ls+选项,特殊情况ls /bin -a,所以必须先
{
	int i = 1;
	for (; i < argc; ++i)
	{
		if (strncmp(argv[i], "-", 1) != 0)
		{
			continue;
		}
		if (strstr(argv[i], "a") != NULL)
		{
			SETOPTION(option, OPTION_A);
		}
		if (strstr(argv[i], "i") != NULL)
		{
			SETOPTION(option, OPTION_I);
		}
		if (strstr(argv[i], "l") != NULL)
		{
			SETOPTION(option, OPTION_L);
		}
	}
}
//打印类型、权限、链接属性、用户、组用户、大小、最后修改时间
//如果只传文件名,就会只是在当前路径下搜索文件有可能找不到其他路径下的文件
void PrintFileInfo(char *path,char *file)//输出文件的详细信息
{
	char filename[128] = { 0 };
	strcpy(filename, path);
	strcat(filename, "/");
	strcat(filename, file);

	struct stat st;
	lstat(filename, &st);
}

void PrintFileName(char *path, char* file)
{
	char filename[128] = { 0 };
	strcpy(filename, path);
	strcat(filename, "/");
	strcat(filename, file);

	struct stat st;
	lstat(filename, &st);

	if (S_ISREG(st.st_mode))
	{
		//判断是否有权限,如果有可执行权限就是‘绿色’;如果不是就是黑色
		if (st.st_mode&S_IXUSR || st.st_mode&S_IXGRP || st.st_mode&S_IXOTH)
		{
			printf("\033[1;32m%s\033[0m  ", file);
		}
		else
		{
			printf("%s  ", file);
		}
	}
	else if (S_ISDIR(st.st_mode))
	{
		//目录文件‘蓝色’
		printf("\033[1;34m%s\033[0m  ", file);
	}
	else if (S_ISLNK(st.st_mode))
	{
		//链接文件‘’
		printf("\033[1;36m%s\033[0m  ", file);
	}
	else if (S_ISFIFO(st.st_mode))
	{
		//管道文件
		printf("\033[1;33m%s\033[0m  ", file);
	}
	else
	{
		//普通文件
		printf("%s  ", file);
	}
}

void PrintFile(char *path)//ls+路径
{
	DIR *dir = opendir(path);
	assert(dir != NULL);

	struct dirent *dt = NULL;
	while (NULL != (dt = readdir(dir)))//说明读取到数据项
	{
		if (!ISSET(option, OPTION_A) && strncmp(dt->d_name, ".", 1) == 0)//是否显示隐藏文件
		{
			continue;
		}

		if (ISSET(option, OPTION_I)//是否显示结点号
		{
			printf("%d ", dt->d_ino);
		}
		if (ISSET(option, OPTION_L)
		{
			PrintFileInfo(path,dt->d_name);//输出文件的详细信息
		}
		PrintFileName(path, dt->d_name);
	}
	printf("\n");
	closedir(dir);
}

int main(int argc,char* argv[])
{
	GetOption(argv, argc);

	int flag = 0;
	int i = 1;
	for (; i < argc; ++i)
	{
		if (strncmp(argv[i], "-", 1) == 0)//如果是-,则就是选项,进入打印
		{
			continue;
		}
		printf("%s : \n", argv[i]);
		PrintFile(argv[i]);//路径
		flag = 1;
	}
	if (!flag)//如果用户没有输入路径,就默认打印当下的路经
	{
		char path[128] = { 0 };
		getcwd(path, 127);
		PrintFile(path);
	}
	exit(0);
}

四、su 切换用户

1、crypt函数

(1)crypt 将明文进行加密与密文进行比较
(2)原型:

char *crypt(const char *key, const char *salt);
  • key:要加密的明文。
  • salt:密钥。
  • salt 默认使用DES加密方法。DES加密时,salt只能取两个字符,多出的字符会被丢弃。
    (3)提示错误

    原因:
    crypt函数不在C的默认函数库中是在 crypt的库里,所以在编译时要这样gcc -o su su.c -lcrypt链接这个库。

2、chmod

chmod a+x su

使任何用户在执行su文件命令时是具有root命令权限的。
具体的操作命令:之后再进行 ./su 操作
在这里插入图片描述

3、setenv(改变或增加环境变量)

(1)此函数并不能添加或修改 shell 进程的环境变量,或者说通过setenv函数设置的环境变量只在本进程,而且是本次执行中有效。
(2)头文件 #include<stdlib.h>
(3)原型: int setenv(const char *name,const char * value,int overwrite);

  • name为环境变量名称字符串。
  • value则为变量内容
  • overwrite用来决定是否要改变已存在的环境变量。
  • 如果没有此环境变量则无论overwrite为何值均添加此环境变量。若环境变量存在,当overwrite不为0时,原内容会被改为参数value所指的变量内容;当overwrite为0时,则参数value会被忽略。返回值 执行成功则返回0,有错误发生时返回-1。

4、代码

#include<sys/types.h>
#include<pwd.h>
#include<shadow.h>
#include <crypt.h>
#include<termios.h>

//su 切换用户
int main(int argc, char* argv[])
{
	char *user = "root";//默认给root用户
	if (argv[1] != NULL)
	{
		user = argv[1];
	}

	//密码操作
	printf("Password: ");
	char password[128] = { 0 };
	//隐式输入密码——取消回显功能
	struct termios oldter, newter;
	tcgetattr(0, &oldter);
	newter = oldter;
	newter.c_lflag &= ~ECHO;
	tcsetattr(0, TCSANOW, &newter);
	fgets(password, 127, stdin);
	tcsetattr(0, TCSANOW, &oldter);//恢复回显
	password[strlen(password) - 1] = 0;

	//用户输入的是明文,但是系统中的是密文,所以要获取密文
	struct spwd *sp = getspnam(user);
	assert(sp != NULL);

	printf("%s\n", sp->sp_pwdp);

	char salt[128] = { 0 };

	//根据sp->sp_pwdp获取salt的值
	int index = 0;
	int count = 0;//记录$的个数
	char *p = sp->sp_pwdp;
	while (*p)
	{
		salt[index] = *p;
		if (salt[index] == '$')
		{
			count++;
			if (count == 3) 
			{
				break;
			}
			p++;
			index++;
		}
	}

	char* mypasswd = (char* )crypt(password, salt);

	if (strcmp(mypasswd, sp->sp_pwdp) != 0)
	{
		printf("password is error,please agin\n");
		exit(O);
	}

	pid_t pid = fork();
	assert(pid != -1);

	if (pid == 0)
	{
		struct passwd *pw = getpwnam(user);
		assert(pw != NULL);
		
		setenv("HOME", pw->pw_dir, 1);//修改环境变量
		
		setuid(pw->pw_uid);//真正的切换用户

		execl(pw->pw_shell, pw->pw_shell, (char*)0);
		perror(pw->pw_shell);
	}
	else
	{
		//等待创建的子进程结束
		wait(NULL);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值