![shell[未完成]](https://sighingnow.github.io/jekyll-gitbook/assets/dinosaur.gif)
shell[未完成]
shell 开发
理解shell如何运作。
-
命名shell 读取输入
-
执行输入命令的程序,使用系统调用
execve
或者库函数execvp
。 -
调用执行后,并没有返回主程序,而是执行完毕回归到了常规shell。这是因为
exec
会把当前程序替换为命令程序。 -
为命令程序创建新进程,但是此时会与主程序的打印错乱,需要等待子程序执行完毕。
-
对于
ls
pwd
echo
cat
命令正常的,但是cd` 无效的。cd
父子进程的工作目录时独立的,在子进程内cd
和父亲没关系。实际上,对于cd
只要修改工作目录即可,不需要放到exec
调用。cd
实际为shell的内置命令, 我们该自己实现,大多内置命令都是通过系统调用,通过help
即可看到:exit
echo
export
。echo
命令也可以实现为内置,而不是调用/bin/echo
-
…
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
void processCommand(char* path, char* argv[])
{
if (strcmp(path, "cd") == 0) {
printf("chdir %s", argv[1]);
chdir(argv[1]); // cd 只有两个参数
} else {
pid_t childpid = fork();
if (childpid == 0) {
execvp(path, argv);
exit(EXIT_SUCCESS);
} else if (childpid < 0) {
puts("fork error");
} else {
wait(NULL); //等待所有子进程结束
}
}
}
void parseCommand(char* line)
{
char* linecpy = strdup(line);
// 获取argc
int argc = 0;
char* token = strtok(linecpy, " ");
while(token) {
token = strtok(NULL, " ");
argc++;
}
// 填充argv, 最后为null
char** argv = malloc((argc + 1) * sizeof(char*));
size_t i = 0;
char* path = strtok(line, " ");
argv[i++] = path;
for (; i < argc; i++)
{
argv[i] = strtok(NULL, " ");
}
argv[i] = NULL;
processCommand(path, argv);
}
int main(int argc, char const *argv[])
{
char* line = NULL;
ssize_t len = 0;
size_t nread = 0;
while (1)
{
printf("dshell>");
nread = getline(&line, &len, stdin);
if (nread > 0 && line[nread - 1] == '\n') {
line[nread - 1] = '\0';
parseCommand(line);
}
if (nread <= 0) {
break;
}
}
return 0;
}
SHELL教程
#!/bin/bash
echo "hello"
两种方式运行:
-
chmod +x ./test.sh
./test.sh
-
作为解释器参数运行,忽略第一行注释。
bash test.sh
SHELL变量
# 定义赋值变量。 !等号两侧不要加空格,空格用于分割命令和参数!
name="Liman"
PI=3.14
_inner="SHANGHAI"
# 使用变量时加$, 推荐!添加{}标识边界
echo $name
echo ${name}
for skill in Ada Coffee Java; do
echo "I am good at ${skill}"
done
name="Oula" # 重新赋值
echo ${name}
# 只读变量
myurl="www.google.com"
readonly myurl
#myurl="www.ggg.com" # 报错,只读变量
# 删除变量,不能删除只读变量
unset name
echo ${name}
# 变量类型
## 字符串:默认类型, '', ""
str='ss'
## 整数:declare, typeset
declare -i myint=22
myint='32' # 尝试转换为int类型
echo $myint
## 数组变量:整数索引,或者关联数组
myarr=(1 2 3 4 'hello')
declare -A associate_arr
associate_arr['name']='lxy'
associate_arr['age']=22
echo ${myarr} # 数组不能直接打印
echo ${associate_arr}
## 环境变量如PATH
echo $PATH
## 特殊变量
echo $0 # 脚本名称
echo $1 # 脚本参数1
echo $2 # 脚本参数2
echo $# # 脚本参数数量
echo $? # 上一个命令退出状态
shell字符串
# shell字符串。
## ''单引号内不解释变量原样输出,内部不出现单独的单引号,成对单引号表示拼接。
## “” 可以解释变量,可以转义
name="liman"
str='I am ${liman}'' hello.'
echo $str
str="I am ${name}, \" hello \" ! \n"
echo $str
echo $name $str # 拼接
echo ${#str} # 字符串长度
echo ${#str[0]} # 与上面等价
echo ${str:1:4} # 提取
echo `expr index "$str" io` # 查找
shell数组
# shell数组
arr=(1 2 'hello')
echo ${arr[0]}
echo ${arr[@]} # @ suoyou yuansu
echo ${#arr[@]} # 数组长度
shell参数选项
# shell传递参数
echo "FILE : $0"
echo "argcount : $#"
echo "arg1 : $1"
echo "arg2 : $2" # 默认空
echo "args : $*" # 一个字符串显示所有参数
echo "pid : $$" # 脚本当前进程
echo "后台运行的最后一个进程id: $!" #
echo "args : $@" # 类似$*
echo "shell当前选项: $-"
echo "最后退出状态: $?" # 命令最后退出状态,0表示没有错误
echo "-- \$* 演示 ---"
for i in "$*"; do
echo $i
done
echo "-- \$@ 演示 ---"
for i in "$@"; do # 等价于传递三个参数
echo $i
done
shell运算
# shell运算
## 数学计算 原生不支持。expr命令实现
val=`expr 2 - 2` # 运算符旁边必须加空格
echo $val
a=1
b=1
val=`expr $a \* $b` # 必须加转义
echo $val
## 关系运算。空格严格! 支持数字,字符串型数字。 -eq -ne -gt -lt -ge -le
if [ $a -eq $b ]
then
echo "$a -eq $b : ok"
else
echo "$a -eq $b : no"
fi
## 布尔运算 ! -o -a, 逻辑运算 $$ ||
if [ $a -gt 20 -o $b -gt 10 ]
then
echo "$a -gt 20 -o $b -gt 10 : ok"
else
echo "$a -gt 20 -o $b -gt 10 : no"
fi
if [ $a == $b ]
then
echo "$a == $b"
fi
if [ $a != $b ]
then
echo "$a != $b"
fi
## 字符串运算。 = != -z -n $
s1="hello"
s2="world"
if [ $s1 = $s2 ]
then
echo "$s1 = $s2"
else
echo "$s1 = $s2, NO"
fi
if [ -z $s1 ]
then
echo " -z $s1: 字符串长度0"
fi
if [ -n $s1 ]
then
echo " -n $s1: 字符串长度不为0"
fi
if [ $s1 ]
then
echo "$s1 为空"
else
echo "$s1 不为空"
fi
## 文件测试运算。 -r -w -x -d
file='/home/dong/shellpractice'
if [ -r $file ]
then
echo "$file can read!"
else
echo "$file not read!"
fi
if [ -d $file ]
then
echo "$file is a directory!"
else
echo "$file is a file!"
fi
if [ -e $file ]
then
echo "$file exist!"
else
echo "$file not exist!"
fi
if [ -s $file ]
then
echo "$file is empty!"
else
echo "$file isn't empty!"
fi
## 自增自减。 本身不支持, let命令, $(()), expr
num=5
let num++
echo $num
num=$((num + 1))
echo $num
((num++))
echo $num
shell echo
# shell echo命令
echo "hello"
echo hello world # 不加引号也行
echo "\"hello world \""
read name # 读取一行
echo "$name hello~"
echo -e "OK! \n" # 开启转义-e, 换行
echo -e "OK! \c" # \c不转换
echo "BYE"
echo "BYE" > t.txt #
echo `date` # 回显命令结果
shell 控制
# shell流程控制
a=10
b=20
if [ $a == $b ]
then
echo "a 等于 b"
elif [ $a -gt $b ]
then
echo "a 大于 b"
elif [ $a -lt $b ]
then
echo "a 小于 b"
else
echo "没有符合的条件"
fi
## in后面是列表
for loop in 1 2 3 4
do
echo "VL : $loop"
done
i=1
while(( $i<=5))
do
echo "while :$i"
((i++))
done
## case esac
read menu
case $menu in
1) echo '你选择了 1'
;;
"GO") echo '你选择了 go'
;;
*) echo '你没有正确输入'
;;
esac
while :
do
echo -n "输入1-5"
read num
case $num in
1|2|3|4|5) echo "input is $num"
;;
*) echo "input error"
break
;;
esac
done
shell函数
[ function ] funname [()]
{
action;
[return int;]
}
# shell函数
## 函数定义必须在前
## 隐式return返回最后一句执行结果,
demofunc(){
echo "demo func!"
}
demofunc
demofuncReturn(){
echo "input num1:"
read num1
echo "input num2:"
read num2
return $((num1+num2))
}
demofuncReturn
echo "RESULT : $? !"
demofuncParam(){
echo "第一个参数为 $1 !"
echo "第二个参数为 $2 !"
echo "参数总数有 $# 个!"
echo "作为一个字符串输出所有参数 $* !"
}
demofuncParam 1 2 3 4
输入输出重定向
命令 | 说明 |
---|---|
command > file | 将输出重定向到 file。 |
command < file | 将输入重定向到 file。 |
command » file | 将输出以追加的方式重定向到 file。 |
n > file | 将文件描述符为 n 的文件重定向到 file。 |
n » file | 将文件描述符为 n 的文件以追加的方式重定向到 file。 |
n >& m | 将输出文件 m 和 n 合并。 |
n <& m | 将输入文件 m 和 n 合并。 |
2>file
command > file 2>&1
将错误和输出合并,重定向到file
command > /dev/null 2>&1
所有/dev/null都会废弃,用来屏蔽输出
shell文件包含
通过 source ./1.sh
或者 . ./1.sh
shell命令支持
通过type
查看
内建命令,shell本身自己实现的:cd echo export alias read let test
系统命令:ls cp。。
环境变量的
echo `ls` # echo ls输出
echo ${ls} # 等价于上面
ls # 会到PATH中查找
type ls # 系统命令
type echo # 内建命令