Shell脚本和编程|青训营笔记

134 阅读6分钟

一、Shell基础概念

1、Shell概念

shell本意是指壳,在计算机术语中,shell是指用户操作接口的意思。操作系统运行起来后都会给用户提供一个操作接口,这个操作接口就叫shell。用户可以通过shell来调用操作系统内部的复杂实现。

图片.png

2、Shell发展

  • 1971年,Ken Thompson(来自贝尔实验室)为UNIX开发了第一个shell,称为 V6 shell

  • Stephen Bourne 在贝尔实验室为 V7 UNIX所开发的Bourne shell,即sh

  • 开源组织GNU为了取代Bourne shell开发的Bourne-Again shell,即Bash

图片.png

3、Shell构成

图片.png

二、Shell命令和语法

1、变量

类型作用域声明方式规范
自定义变量当前shell=(隐式声明)字符串、整型、浮点型、日期型
环境变量当前shell及其子shellexport declare -x(显示声明)
系统环境变量所有shell启动加载

图片.png

2、自定义变量

#!/bin/bash
# 这是脚本文件的shebang,它告诉系统脚本文件使用哪种解释器来执行。在这个例子中,使用的是bash解释器#变量名=变量值(等号左右不能有空格),定义了两个变量page_size和page_num,并分别给它们赋值为1和2
page size=1
page_num=2
​
#将命令复制给变量,这样可以通过变量名来执行命令。
_ls=ls#将命令结果赋值给变量,可以通过变量名来访问命令结果
file_list=$(ls -a)
​
#默认字符串,不会进行 + 运算。所以total的值为"page_size*page_num"字符串
total=page_size*page_num
​
#声明变量为整型(-i),后续的数值计算中,total会被当作整型来处理
let total=page_size*page_num
declare -i total=page_size*page_num
​
#导出环境变量,使得其在子进程中也可用
export total
declare -x total

3、系统环境变量

图片.png

  • $#表示当前脚本或函数的参数个数。
  • $*表示当前脚本或函数的所有参数列表。
  • $?表示上一个命令的退出状态码。
  • $PS1表示shell提示符的格式。
  • $HOME表示当前用户的主目录。
  • $PATH表示系统的可执行文件路径列表。

4、配置文件加载

图片.png

5、运算符和引用

图片.png

6、管道

管道与管道符 ,作用是将前一个命令的结果传递给后面的命令

语法:cmd1 | cmd2

要求:管道右侧的命令必须能接受标准输入才行,比如grep命令,ls、mv等不能直接使用,可以使用xargs预处理

注意:管道命令仅仅处理stdout,对于stderr会予以忽略,可以使用set -o pipefail设置shell遇到管道错误退出。

#!/bin/bash#该命令将文件platform.access.log的内容通过管道符|传递给grep命令,用于查找文件中包含字符串ERROR的行。这个命令的作用是在platform.access.log文件中查找错误信息
cat platform.access.log | grep ERROR
​
#该命令使用netstat命令来显示所有的网络连接状态,并通过管道符|将结果传递给grep命令,用于查找所有处于ESTABLISHED状态的连接。最后,通过管道符|将结果传递给wc -l命令,用于统计行数。这个命令的作用是统计当前系统中所有处于ESTABLISHED状态的网络连接数量
netstat -an grep ESTABLISHED | wc -l
​
#该命令使用find命令在当前目录中查找所有扩展名为.sh的文件,并通过管道符|将结果传递给xargs命令,将查找到的文件名作为参数传递给ls -l命令,用于显示这些文件的详细信息。这个命令的作用是列出当前目录中所有扩展名为.sh的文件的详细信息
find . -maxdepth l -name "*.sh" | xargs ls -l

7、重定向

输出重定向符号:
>:覆盖写入文件
>>:追加写入文件
2>:错误写入文件
&>:正确和错误输出统一到文件中
输入重定向符号:
<
<<

图片.png

8、判断命令

shell中提供了test、[、[[三种判断符号,可用于:

  • 整数测试
  • 字符串测试
  • 文件测试

语法:

  • test condition
  • [ condition ]
  • [[ condition ]]

注意:

  • 中括号前后要有空格符
  • [ 和test是命令,只能使用自己支持的标志位,<、>、=只能用来比较字符串
  • 中括号内的变量,最好都是用引号括起来
  • [[更丰富,在整型比较中支持<、>、=,在字符串比较中支持=~正则
#!/bin/bash# 整数测试
test $nl -eq $n2   # 测试 $nl 是否等于 $n2
test $nl -lt $n2   # 测试 $nl 是否小于 $n2
test $nl -gt $n2   # 测试 $nl 是否大于 $n2# 字符串测试
test -z $str_a     # 测试 $str_a 是否为空
test -n $str_a     # 测试 $str_a 是否非空
test $str_a = $str_b   # 测试 $str_a 是否等于 $str_b# 文件测试
test -e /dmt && echo "exist"   # 测试 /dmt 是否存在,并在存在时输出 "exist"
test -f /usr/bin/npm && echo "file exist"   # 测试 /usr/bin/npm 是否存在,并在存在时输出 "file exist"

9、分支语句

  • if语句用于判断条件是否成立,如果成立则执行相应的命令或者代码块。

if语句的基本语法如下:

if [ condition ]
then
    command1
    command2
    ......
    commandN
fi# condition是需要判断的条件,可以是变量、字符串、数值等,command1到commandN是需要执行的命令或者代码块
  • case语句用于根据不同的条件执行相应的命令或者代码块。
case variable in
pattern1)
    command1
    ;;
pattern2)
    command2
    ;;
......
*)
    commandN
    ;;
esac
​
# variable是需要判断的变量,pattern1到patternN是需要判断的模式,command1到commandN是需要执行的命令或者代码块。如果没有任何一个模式匹配,那么就会执行*后面的命令或者代码块。esac是用来结尾的

10、循环

(1)for循环语句

读取不同的变量值,用来逐个执行同一组命令。

  • 用法:

读取不同的变量值,用来逐个执行同一组命令

for循环经常使用在已经知道要进行多少次循环的场景

  • 格式:
for  变量名  in  取值列表
do
	命令序列
done

图片.png

案例一:循环出0到10之间的整数

法一:
#!/bin/bash
for i in {1..10}
  do
    echo $i
  done
 
法二:
#!/bin/bash
for i in $(seq 1 10)
do
  echo $i
done

输出结果:

图片.png

案例二:求20以内偶数和

#!/bin/bash
sum=0
for i in {2..20..2}
do
        sum=$[sum+i]
        let i++
done
echo "20以内的偶数和为:$sum

输出结果:

图片.png

案例三:根据ip地址列表检查主机状态

#!/bin/bash

IPLIST=$(cat /root/shell/ip.txt)
for IP in $IPLIST
do
ping -c 3 -i 0.5 $IP &> /dev/null
        if [ $? -eq 0 ];then
        echo "host $IP is up."
      else
        echo "host $IP is down."
      fi
done    

输出结果:

图片.png

(2)while循环

只要条件成立,则反复循环,不成立即停止。

while 条件测试操作
do
   命令序列
done

图片.png

案例一:利用while循环打印一个5x5的*

#!/bin/bash
i=1
j=1
  while [ $i -le 5 ]
do
     while [ $j -le 5 ] 
    do
  echo -n "* " 
  let j++
    done
       echo 
       let i++
       let j=1
done

输出结果:

图片.png

案例二:while 猜数字小游戏

#!/bin/bash

pc_num=$[RANDOM%3+1]
count=0
while true
do
   read -p "请输入一个数字:" user_num
   if [ $user_num -eq $pc_num ]
   then
      echo "答对啦"
      break
   elif [ $user_num -gt $pc_num ]
   then
      echo "你的数字太大了"
   else
      echo "你的数字太小了"
   fi
   let count++
done
   echo 你输入的次数为:$count

输出结果:

图片.png

(3)until循环语句

重复测试某个条件,只要条件不成立则反复循环。

while 条件测试操作
do
   命令序列
done

图片.png

案例:求50到100的和

#!/bin/bash
sum=0
i=50
until [ $i gt 100]
 do
  sum=$[sum+i]
  let i++
done
echo "{50..100}的和:$sum"

输出结果:

图片.png

(4)扩充的知识点

break 和 cotinue 命令

图片.png

图片.png

11、模块化

  • 模块化的原理是在当前shell内执行函数文件
  • 方式:source[函数库的路径]

图片.png

12、常用命令

图片.png

三、Shell执行过程和原理

1、执行过程

(1)shell脚本一般以.sh结尾,也可以没有,这是一个约定;第一行需要指定用什么命令解释器来执行

#! /bin/bash
复制代码
#! /user/bin/env bash

(2)启动shell的三种方式

#文件名运行(子进程执行)
./filename.sh
​
​
#解释器运行(子进程执行)
bash ./filename.sh
​
​
# source运行(当前进程中执行)
source ./filename.sh

(3)执行过程

  • 字符解析

    • 识别换行符、分号(;)做行的分割
    • 识别命令连接符(||&&管道)做命令的分割
    • 识别空格、tab符,做命令和参数的分割
  • shell展开,例如{1...3}解析为1 2 3

  • 重定向,将stdin、stdout、stderr的文件描述符进行指向变更

  • 执行命令

    • builtin直接执行
    • 非builtin使用$PATH查找,然后启动子进程执行
  • 收集状态并返回给脚本

图片.png

2.shell展开

  • Shell 展开是指在执行命令之前,Shell 预处理命令行中的各种特殊字符,将它们替换为实际的值或执行相应的操作

(1)大括号展开

  • 一般由三部分构成,前缀、一对大括号、后缀,大括号内可以是逗号分割的字符串序列,也可以是序列表达式{x..y[..incr]}
#字符串序列
a(b,c,d)e => abe ace ade
​
#表达式序列,(数字可以使用incr调整增量,字母不行)
{1..5} => 1 2 3 4 5
​
{1..5..2} => 1 3 5
​
{a..e} => a b c d e

(2)波浪号展开

#当前用户主目录
~ => $HOME
​
~/fo => $HOME/foo
​
#指定用户的主目录
-fred/foo=>用户fred的SHOEM/foo
​
#当前工作目录
~+/foo => $PWD/foo
​
#上一个工作目录
~-/foo => ${$OLDPWD-'~-')/foo

(3)参数展开

  • 间接参数扩展${!parameter},其中引用的参数并不是parameter而是parameter的实际的值
parameter="var"  # 定义一个名为 parameter 的变量,其值为字符串 "var"
var="hello"  # 定义一个名为 var 的变量,其值为字符串 "hello"
echo ${!parameter}  # 使用参数展开的形式,将变量 $parameter 的值 "var" 替换为 $var,从而得到变量 $var 的值 "hello",并将其输出到标准输出
​
#输出 hello
  • 参数长度${#parameter}

  • 空参数处理

  • S{parameter:-word}#为空替换 
    
  • {parameter:=word#为空替换,并将值赋给parameter变量
    
  • parameter:?word)#为空报错 
    
  • {parameter:+word}#不为空替换
    
a=1  # 定义一个名为 a 的变量,其值为整数 1
​
echo ${a:-word} #1  # 输出变量 $a 的值,如果 $a 未定义或为空,则输出 "word"由于 $a 已经定义并赋值为 1,因此输出为 1
​
echo $(b:-word) #word  # 执行命令替换,执行命令 $b 并将其输出作为结果,如果 $b 未定义或为空,则输出 "word"由于 $b 未定义,因此输出为 "word"
​
echo $(par:=word) #word  # 执行命令替换,将变量 $par 的值设置为 "word",并将其输出作为结果由于 $par 未定义,因此输出为 "word"
​
echo $(par:-hello) #word  # 执行命令替换,输出变量 $par 的值,如果 $par 未定义或为空,则输出 "hello"由于 $par 未定义,因此输出为 "word"
​
echo $(par:+foo) #foo  # 执行命令替换,如果 $par 已经定义且非空,则输出 "foo",否则不输出任何内容由于 $par 未定义,因此不输出任何内容
​
#输出分别为:1 word word word foo
复制代码
  • 参数切片

    {parameter:offset) {parameter:offset:length}

  • 参数部分删除

  • {parameter%word} #最小限度从后面截取word 
    
  • {parameter%%word}#最大限度从后面截取word 
    
  • S{parameter#word}#最小限度从前面截取word
    
  • ${parameter##word#最大限度从前面截取word
    
#!/bin/sh
​
str=abcdefg  # 定义一个名为 str 的变量,其值为字符串 "abcdefg"
​
spl=$(str##*d)  # 执行命令替换,删除变量 $str 开头到最后一个 "d" 之前的所有字符,并将剩余的字符串赋值给变量 $spl。由于变量 $str 的值为 "abcdefg",因此 $spl 的值为 "efg"。
# * 是通配符的一种,表示匹配任意数量的任意字符(包括 0 个字符)。具体来说,*d 表示匹配以字母 “d” 结尾的字符串,并将匹配到的字符串删除。而 ## 表示从变量的开头开始匹配,删除最长的匹配项。
​
sp2=${str%%d*}  # 执行参数展开,删除变量 $str 最后一个 "d" 以及之后的所有字符,并将剩余的字符串赋值给变量 $sp2。由于变量 $str 的值为 "abcdefg",因此 $sp2 的值为 "abc"。echo $spl #输出efg  # 输出变量 $spl 的值,即字符串 "efg"。echo $sp2 #输出abc  # 输出变量 $sp2 的值,即字符串 "abc"。# echo的两个输出分别如下:efg abc

(4)命令替换

  • 在子进程中执行命令,并用得到的结果替换包裹的内容,形式上有两种:$(...)或...
#! /bin/bashecho ${whoimi}  # 输出变量 $whoimi 的值。由于 $whoimi 未定义,因此输出为空。foo(){  # 定义一个名为 foo 的函数
echo "asdasd"  # 在函数中输出字符串 "asdasd"
}
​
a=`foo`  # 执行命令替换,执行函数 foo 并将其输出作为结果,将结果赋值给变量 $a。由于函数 foo 的输出为 "asdasd",因此变量 $a 的值为 "asdasd"。

(5)数学计算

  • 使用$( ( ) ) 包裹数学运算表达式,得到结果并替换
#! /bin/bashecho $((1+2)) # 3

(6)文件名展开

  • 当有单词没有被引号包裹,且其中出现了'*','?',and '[' 字符,则shell会去按照正则匹配的方式查找文件名进行替换,如果没找到则保持不变
#! /bin/bash
​
$ echo D*
​
# 输出当前目录下所有以 D字母开头的目录、文件

四、调试和前端集成

1、调试

  • 普通log,使用echo、printf
  • 使用set命令
  • vscode debug插件
set配置作用补充
-u遇到不存在的变量就会报错,并停止执行。-o nounset
-x运行结果之前,先输出执行的那一行命令。-o xtrace
-e只要发生错误,就终止执行-o errexit
-o pipefail管道符链接的,只要一个子命令失败整个管道命令就失败,脚本就会终止执行。
#! /bin/bash#一般在最前面就进行配置
set -uxe -o pipefail
​
echo "hello world"

2、Vscode插件配置

  • shellman:代码提示和自动补全

  • shellcheck:代码语法校验

  • shell-format:代码格式化

  • Bash Debug:支持单步调试

    • 安装vscode插件
    • 编写launch.json文件
    • 升级bash到4.x以上版本

图片.png

3、前端集成

(1)node中通过exec、spawn调用shell命令

(2)shell脚本中调用node命令

(3)借助zx等库进行javascript、shell script的融合

  • 借助shell完成系统操作,文件io、内存、磁盘系统状态查看
  • 借助nodejs完成应用层能力,网络io、计算等

图片.png

图片.png

五、文章学习大纲

图片.png