shell[未完成]

shell[未完成]

shell 开发

参考

理解shell如何运作。

  1. 命名shell 读取输入

  2. 执行输入命令的程序,使用系统调用execve 或者库函数execvp

  3. 调用执行后,并没有返回主程序,而是执行完毕回归到了常规shell。这是因为exec会把当前程序替换为命令程序。

  4. 为命令程序创建新进程,但是此时会与主程序的打印错乱,需要等待子程序执行完毕。

  5. 对于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"

两种方式运行:

  1. chmod +x ./test.sh ./test.sh

  2. 作为解释器参数运行,忽略第一行注释。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 # 内建命令

results matching ""

    No results matching ""