一、预习
linux基础命令
- 创建文件
touch test.sh - 查看文件
cat test.sh - 显示当前目录
pwd - 列出当前路径中的文件和目录
ls - 在当前目录查找sh后缀文件
find ./ -name "*.sh" - 默认显示文档的最后10行
tail test.sh - 查找文件中的关键字
grep "echo" test.sh - 移动文件到上一级目录
mv test.sh ../ - 多条命令执行
cat test.sh;pwd;ls - 有条件的执行多条命令
命令一 &&(||) 命令二 &&(||) 命令三 - 管道命令
echo "Hello World" | cat > hello.txt
shell基础语法
- 以
#开头的行就是注释,会被解释器忽略 ${name}表示使用name这个变量的值- 字符串可以用单、双引号,也可以不用
- 拼接字符串
echo ${name}${year} - 获取字符串长度
echo ${#name} - 截取字符串
echo ${name:0:3},范围为左闭右开 - 定义数组
name=(name1 name2 name3) - 取数组第2项
echo ${name[2]} - 取全部的数组
echo ${name[@]} - 变量赋值
len=${#name[@]}
shell文件运行
shell文件一般以.sh结尾,也可以没有,通常约定第一行需要指定用什么命令解释器来执行#! /bin/bash,运行命令有以下三种:
./test.shbash ./test.shsource ./test.sh
linux中第一种方式不能直接运行,可以通过linux命令chmod +x test.sh赋予运行权限,然后再运行。
二、终端、tty、shell和bash的区别
终端
作用是提供一个命令的输入输出环境,linux中就是termimal
tty
早期指电传打印机,现在基本等同终端,也是提供一个命令的输入输出环境
shell
shell是一个命令行解释器,shell接收用户或者其他应用程序的命令, 然后将这些命令转化成内核能理解的语言并传给内核, 内核执行命令完成后将结果返回给用户或者应用程序。当你打开一个terminal时,操作系统会将terminal和shell关联起来,当我们在terminal中输入命令后,shell就负责解释命令。
bash
linux系统上可以包含多种不同的shell,常见的有Bourne shell (sh)、C shell (csh) 和 Korn shell (ksh),bash是Bourne shell的扩展,是增强的shell。
三、shell详细语法
声明变量类型declare
# 乘法例子
a=2
b=3
declare -i c=a*b
echo ${c}
系统环境变量
配置文件加载
运算符和引用
判断命令
test condition[ condition ][[ condition ]]
# 整数测试
test $n1 -eq $n2
test $n1 -lt $n2
test $n1 -gt $n2
# 字符串测试
test -z $str_a # 字符串的长度为零则为真
test -n $str_a # 字符串的长度不为零则为真
test $str_a = $str_b # 判断两个字符串是否相等
# 文件测试
test -e /dmt && echo "exist" # test -e 如果文件存在则为真
test -f /usr/bin/npm && echo "file exist" # test -f 如果文件存在且为普通文件则为真
# 1.中括号前后要有空格
# 2. test和中括号都只能使用自己支持的标志位,<,=,>只能用来比较字符串
# 3.双中括号功能最多,支持整形比较<、=、>,支持字符串正则=~
shell语句和函数
# 分支语句 if-else
x=1
y=2
if [ $x -eq $y ]
then echo "x和y相等"
elif [ $a -gt $b ]
then echo "x大于y"
elif [ $a -lt $b ]
then echo "x小于y"
else
echo "其他"
fi
# -gt 表示大于,-lt表示小于,[和$x之间一定要有空格
# then和fi是语法,必须要加
# 分支语句 case
choice="3"
case ${choice} in
"1")
echo "number 1"
;;
"2")
echo "number 2"
;;
*)
echo "default"
;;
esac
# 循环语句 for
for((i=1;i<5;i++));do
echo "i="${i}
done
arr=(a b c d)
for j in ${arr[@]};do
echo "j="${j}
done
# 第一种for循环遍历要加两个括号,第二种for循环遍历的是数组,要加${}
# 循环语句 while
time=1
while(($time<=3))
do
echo $time
let "time++"
done
# 循环语句 until
time=1
until([ $time -gt 3 ])
do
echo $time
((time++))
done
# []中括号用于判断内部表达式,只支持<=、==、>=判断字符串,判断数字需要使用-gt、-eq、-lt
# 函数
function add(){
# 如果参数个数等于两个
if [ $# -eq 2 ];then
# local是定义局部变量 $1表示第一个参数
local num1=$1
local num2=$2
return $(($num1+$num2))
# unset num1 如果前面没有使用local设定局部变量,可以通过unset将其限定在这个函数中
else
exit 1
fi
}
# 两种调用方法
add 1 2
echo $? # $?查看上一条命令状态,如果是return返回,那$?就是return的值,如果是exit返回就是exit返回的值
res=$(add 1 2)
# 编写成函数一个文件的话,可以通过`source function.sh`命令引入进来,相当于import
常用命令
使用shell日志查询
# 查看test的日志文件中ERROR关键字所在的行,然后按照空格分割后第三例的时间排序
cat test.log | grep -n "ERROR" | sort -t " " -k 3
# 查看ERROR行的前三行和后三行,A表示after,B表示before
grep "ERROR" test.log -A3 -B3
四、执行过程和原理
执行过程
绿色的为shell文件,橙色的为内核,从开始执行到完成总共需要经过5步:
- 字符解析(Lexical analysis and parse)
- 识别换行符、分号做行的分割
- 识别命令连接符(|| && 管道)做命令的分割
- 识别空格、tab符,做命令和参数的分割
- shell展开(Expansion)
-
大括号展开
-
# 字符串序列 a{b,c,d}e => abe ace ade #表达式序列,数字可以调整增量,字母不行 {1..5} => 1 2 3 4 5 {1..5..2} => 1 3 5 {a..e} => a b c d e -
波浪号展开
# 当前用户主目录
~ => $HOME
~/foo => $HOME/foo
# 指定用户主目录
~fred/foo => 用户fred的$HOME/foo
# 当前工作目录
~+/foo => $PWD/foo
# 上一个工作目录
~-/foo => ${$OLDPWD-'~-'}/foo
-
参数展开
1.间接参数扩展${!parameter},其中引用的参数是Parameter实际表示的值 2.参数长度${#parameter} 3.空参数处理${parameter:-word}为空替换 4.参数切片${parameter:offset:length} 5.参数部分删除${parameter%word}最小限度从后面截取word -
命令替换
# 主要是``反引号解析和$()解析
echo ${whoimi}
foo(){
echo "asdasd"
}
a=`foo`
在子进程中执行命令,并用得到的结果替换包裹的内容
- 数学计算
echo $((1+2))
- 文件名展开
$ echo D*
D*没有以{}或者”“包裹,shell就会认为其是文件然后到当前目录去查找 3. 重定向(Redirection) - 将stdin、stdout、stderr的文件描述符进行指向变更 4. 执行命令(Excute builtin) - builtin 直接执行 - 非builtin 使用$PATH查找,然后启动子进程执行 5. 收集状态并返回
五、调试和前端集成
调试方法
- 使用echo、printf
- 使用set命令
- vscode debug插件
#! /bin/sh
set -uxe -o pipefail
...
前端集成
- node中通过exec、spqwn调用shell命令
- shell脚本中调用node命令
- 借助zx等库进行js、shell脚本的融合
- 借助shell完成系统操作、文件io、内存、磁盘系统状态查询等
- 借助node完成应用层能力、网络io、计算等
六、学习总结
shell是一种只能支持较为简单逻辑的、可以直接把任意现有程序当作函数无缝集成的“超高级语言”;但因为命令行模拟这个本质,它的语法较为笨拙,很难像正规的脚本语言那样得到很多很多的语法糖或者其它便利。