1、shell作用:(1)打出提示符,获取用户输入的指令;(2)解析命令,分解命令及参数,处理字符‘|’管道,‘&’后台,‘>’,‘<’;(3)寻找命令文件,对照PATH中‘:’隔开的路径寻找。(4)执行命令
//(1)打出提示符,获取用户输入的指令
char *path;
path = get_current_dir_name();
printf("%s>$",path);
lc_char = getchar();
while(lc_char != '\n' )
{
buffer[size++] = lc_char;
lc_char = getchar();
}
buffer[size]='\0';
input = (char *)malloc(sizeof(char) * (size+1));
strcpy(input, buffer);
}
//(2)解析命令
int i,j=0;
for(i=0;j<size;i++)
{
//从定向处理
if(input[i]=='<' || input[i]=='>')
**redirect(input, size);**
//管道处理
else if(input[i]=='|')
**pipel(input, size);**
if(input[i]==' ', || input[i]=='\t', || input[i]== '\0')
{
//处理连续空格和tab
if(j==0)
continue;
//根据空格把多个参数分散存在 *arg中
else
{
buffer[j++]='\0';
arg[k] = (char *)malloc(sizeof(char) * j);
strcpy(arg[k],buffer);
j=0;
k++;
}
}
else
{
//标记后台运行
if(input[i]=='&' && input[i+1]=='\0')
{
is_back =1;
continue;
}
buffer[j++]=input;
}
}
//(4)执行命令
if((pid = fork())==0)
exec(buffer, arg);
else
if(is_back == 0)//新进程不在后台运行,父进程必须等起结束
waitpid(pid, &status, 0);
//返回父进程是释放申请的空间
for(i=0;i<k;i++)
free(arg[i]);
2、新进程会继承父进程的文件描述符,这就是为什么并发进程会读写同一个键盘和显示器。
重定向实现:
//重定向
if(in[i]=='<')
ls_in=1;
if(in[i]=='>')
is_out=1;
if(ls_in)
{
fd_in=open(filename[is_in], O_RONLY, S_IRUSR|S_IWUSR);
dup2(fd_in, STDIN_FILENO);//把fd_in所指向的文件描述符复制到 STDIN_FILENO,STDIN_FILENO已打开则先关闭
}
if(ls_OUT)
{
fd_out=open(filename[is_out], O_WRONLY|O_CREATE|O_TRUNC, S_IRUSR|S_IWUSR);
dup2(fd_out, STDOUT_FILENO);
}
3、普通管道,子进程可以通过类型继承打开文件描述符的方式继承管道。而命名管道,进程通过使用和管道连接的类似文件名的字符串获得管道。当进程使用命名管道,该管道就成了系统资源。
//命令中管道实现
int fd[2];
if((child1 = fork())==0)
{
close(fd[0]);//关闭读端
//将标准输出重定向到管道的写端,这样子进程的输出就写入了管道
dup2(fd[1],STDOUT_FILENO);
close(fd[1]);
execv(buffer, argv[0]);
}
else{
//父进程等待写入管道的进程结束
waitpid(child1, &li_comm, 0);
//管道以字节流的形式读入读出,我们可以写入一个结束标记,告诉读进程数据到这就结束了
end[0]=0x1a;
write(fd[1],end,1);
close(fd[1]);
}
if((child2 = fork())==0)
{
//将标准输入重定向到管道的读端
dup2(fd[0],STDIN_FILENO);
close(fd[0]);
execv(buffer, argv[1]);
}
4、shell变量
首行指明使用哪种shell
#!/bin/bash
只读变量,字符串可以用单引号,也可以用双引号,也可以不用引号
str="http://www.w3cschool.cc"
readonly str
删除变量,只读变量不能删除
unset str1
用单引号,不进行转义或取变量
echo '$name\"'
$name\"
双引号里面可以有变量和转义字符
your_name='qinjx'
str="Hello, I know your are \"$your_name\"! \n"
echo $str
$Hello, I know your are "qinjx"! \n
获取字符串长度
echo ${#string}
提取子字符串,从字符串第 2 个字符开始截取 4 个字符
echo ${string:1:4}
查找子字符串,查找字符 "i 或 s" 的位置,下面为反引号:
echo `expr index "$string" is`
$# 传递到脚本的参数个数
$* 以"$1 $2 … $n"的形式输出所有参数
$? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误
Shell 数组用括号来表示,元素用"空格"符号分割开
array_name=(value1 ... valuen)
获取数组中的所有元素
${my_array[*]}
${my_array[@]}
获取数组的长度
${my_array[#*]}
${my_array[#@]}
$* 与 $@ 区别:
相同点:都是引用所有参数。
不同点:合成单个参数,多个参数
5、shell运算
原生bash不支持简单的数学运算,expr 是一款表达式计算工具,使用它能完成表达式的求值操作
计算2+2,2和‘+’之间要有空格
val=`expr 2 + 2`
条件表达式要放在方括号之间,并且要有空格
例如: [$a==$b] 是错误的,必须写成 [ $a == $b ]。
乘号(*)前边必须加反斜杠(\)才能实现乘法运算
val=`expr $a \* $b`
a<20 || b>100
-o 或运算, [ $a -lt 20 -o $b -gt 100 ]
-a 与运算
-z 检测字符串长度是否为0,为0返回 true。 [ -z $a ] 返回 false。
-n 检测字符串长度是否为0,不为0返回 true。 [ -n $a ] 返回 true。
str 检测字符串是否为空,不为空返回 true。 [ $a ] 返回 true。
开启转义 -e,(-e 文件名 如果文件存在则为真)
echo -e "OK! \n"
echo "It it a test"
$
OK!
It it a test
printf不会像 echo 自动添加换行符,我们可以手动添加 \n
printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg
-10s 指一个宽度为10个字符(-表示左对齐,没有则表示右对齐)
%6.2f后,小数部分为两位,总长度6位,前面补空格