一、实现操作命令
上一个我们只是简单的写了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 是终端默认颜色
前景 | 背景 | 颜色 |
---|---|---|
30 | 40 | 黑色 |
31 | 41 | 紅色 |
32 | 42 | 綠色 |
33 | 43 | 黃色 |
34 | 44 | 藍色 |
35 | 45 | 紫紅色 |
36 | 46 | 青藍色 |
37 | 47 | 白色 |
代码 | 意义 |
---|---|
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);
}
}