Linux21--shell编程基础

1 shell 编程

1.1 shell 介绍

# 1 shell 介绍
1.shell 编程是一门解释型、弱类型、动态的编程语言

2.shell 就是一个命令解释器
  shell分为交互式和 非交互式
   # 通过 $- 来查看   $- 记录的是当前配置 打开的shell选项
     himBH  # 交互式shell
          h   # hashall      当shell运行时,会记录保存执行过的命令   hash缓存表
          i   # interactive  交互式shell的意思
          m   # monitor      监控后台进程的状态  监控模式
          B   # braceexpand  大括号拓展,支持{} 整体、序列
          H   # history      历史命令的记录
      
      hB    # 非交互式shell

   # 通过 set -o 查看来确认shell选项的打开状态  set -o | grep -w on


# 2 类比
shell语法   <===>  python语法
bash解释器  <===>  python解释器
平台        <===>  平台


# 3 shell 脚本
1.系统命令的堆积
2.特定的格式,特定的语法,组成的一个文件
3.以.sh为结尾的


# 4 运行shell脚本
权限:
  当前用户需要对脚本文件 有读和执行 r+x权限

方式:
  1. bash  脚本的文件路径
  2. 脚本的文件绝对路径  或 ./文件相对路径

1.2 命令优先级

# 命令的执行优先级
大致分成三类: 别名、内部命令、外部命令


==> alias                            # 别名   alias可以查看
  ==> Compound Commands   复合命令  # eg: for if while等 
    ==> function  函数            # 可通过 set 找到  eg: set |grep cd
      ==> build_in   内置       # 是bash解释器 自带的功能 
        ==> hash
          ==> $PATH          # 外部命令  环境变量中,按冒号分割的每个路径中去搜索
            ==> error: command not found

2 变量

2.1 基本使用

### 交互式环境中
[root@localhost ~]# x=1                # 1 定义变量  中间不能有空格
[root@localhost ~]# x=2
[root@localhost ~]# echo $x            # 2 使用变量方式一:'$变量' 


[root@localhost ~]# name=egon
[root@localhost ~]# echo $name
[root@localhost ~]# echo ${name}       #  3 使用变量方式二:'${变量}'     推荐使用
                                            是将一个变量名看作是一个整体
[root@localhost ~]# echo "hello $name1"     # 找不到name1的变量
hello 
[root@localhost ~]# echo "hello ${name}1"
hello egon1

# 删除变量
unset VARNAME

2.2 引号对变量的影响

### 1 双引号 ---> 弱引用   保留变量
[root@localhost ~]# name=egon
[root@localhost ~]# echo "hello $name"
hello egon


### 1 单引号 ---> 强引用    都视为普通字符
[root@localhost ~]# name=egon
[root@localhost ~]# echo 'hello $name'
[root@localhost ~]# echo "hello \$name"   # 或者使用转义符
hello $name


### 1 反引号 ---> 取命令的执行结果
[root@localhost ~]# today=`date +%F`
[root@localhost ~]# echo $today
2020-08-11

# $():也可以取命令的结果    与反引号的区别是 可以用于嵌套使用
[root@localhost ~]# today=$(date +%H:%M:%S)
[root@localhost ~]# echo $today
22:05:55
        
# eg:
[root@localhost ~]# tar czf `date +%F`_bak.tar.gz /tmp

2.3 变量作用域

### 1 变量作用域
环境变量   : 在当前shell及子shell生效!

自定义变量 : 仅在当前shell 进程生效!

# eg1:环境变量
[root@localhost ~]# x=1000
[root@localhost ~]# export x    # 声明成环境变量
[root@localhost ~]# bash        # 启动新的子shell
[root@localhost ~]# echo $x
1000


# eg2:自定义变量
[root@localhost ~]# x=1000       # 自定义一个全局变量
[root@localhost ~]# vim b.sh     # echo $x
[root@localhost ~]# bash b.sh    # 并不能打印出x的值,是启动一个子shell执行代码
[root@localhost ~]# 
[root@localhost ~]# source b.sh  # 能打印出x的值,source作用是将b.txt的代码,复制出来,在当前shell环境执行
1000



### 2 查看变量
set  # 查看所有变量(包括自定变量和环境变量) 
env  # 查看环境变量


### 3 系统环境变量配置文件
1. /etc/profile
2. /etc/bashrc

3. ~/.bashrc
4. ~/.bash_profile


### 4 常见系统环境变量
[root@localhost ~]# echo $PS1      # 命令提示符
[\u@\h \W]\$

[root@localhost ~]# echo $HOSTNAME
localhost.localdomain

[root@localhost ~]# echo $USER
root

[root@localhost ~]# echo $UID
0

[root@localhost ~]# echo $SHELL
/bin/bash

[root@localhost ~]# echo $HISTSIZE   # 执行命令 历史记录的保存数量
1000

[root@localhost ~]# echo $MAIL
/var/spool/mail/root

[root@localhost ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin

3 元字符

bash中的特殊字符,键盘上能敲出来的特殊字符都有其特殊意义,强调一点:元字符是被shell解释的

3.1 常见的特殊字符

# 1  ~   家目录

# 2  .与..
  .    表示当前路径
  ..   表示父级路径

# 3  ! 与 ^  都是取反

# 4  @  无特殊意义

# 5  #  注释

# 6  $  取变量值
     注: $?   # 判断上一条命令的结果是否为真,0表示true


# 7  *   任意多个字符
     ?  任意一个字符
     []  范围中的任意一个字符
       eg:[12] [ac] [0-9] [a-z]  # a到z 是不区分大小写的
       
     
# 8  _  无特殊意义,可用于名字的声明

# 9  /  路径分隔符

# 10  &  后台运行


# 11  |  管道:把一个进程的处理结果传递给另外一个进程        详见 管道
     xargs 参数传递,把上一个命令的结果作为下一个命令的参数
     
     
# 12  ( )  在子shell中执行 
     $( )   可以取命令的结果
     $(( ))  整数运算


# 13  \   转义特殊字符   取消符号的特殊意义
      \\    \自身
      \$    转义$
      \t    制表符
      \b    退格符
      \r    回车符
      \n    换行符
      \c    取消换行


# 14  ;  &&  ||  连接多条命令
      ;   # 不论前一条命令运行成功与否,都会执行后续命令
      &&  # 前一条命令 执行成功,才会执行后续命令        与
      ||  # 前一条命令 执行不成功,才会执行后续命令      非  
      
      
# 15  :   也是一天命令,永远为真的命令  跟true一样
      while :
      while true 
      
      
# 16 > >>  输出重定向  覆盖  追加     详见 输出与重定向
     < <<  输入重定向

3.2 数学运算符

### 0  + - * / % 运算符
  /   # 除法 求商
  %   # 除法 取余
  
  
### 1 bc是比较常用的linux计算工具了,而且支持浮点运算
yum install bc -y
[root@localhost ~]# res=`echo 1+1 | bc`
[root@localhost ~]# echo $res
2

[root@localhost ~]# res=`echo 10 % 3 | bc`
[root@localhost ~]# echo $res
1

[root@localhost ~]# res=`echo 1.2+1.3 | bc`
[root@localhost ~]# echo $res
2.5

[root@localhost ~]# res=`echo "scale=2;5.0/3.0" | bc`   # scale 表示保留小点数位数,且不四舍五入
[root@localhost ~]# echo $res
1.66

[root@localhost ~]# res=`echo "scale=2;5.0/6.0" | bc`    # 注意0.几  个位不会有0
[root@localhost ~]# echo $res   
.83


### 2 expr不支持浮点数计算。而且要注意数字与运算符中的空格
[root@localhost ~]# res=`expr 5 / 3`  # 不支持浮点计算
[root@localhost ~]# echo $res
1

[root@localhost ~]# res=`expr 1+1`   # 注意:计算符号前后要有空格
[root@localhost ~]# echo $res
1+1

[root@localhost ~]# res=`expr 1 + 1`
[root@localhost ~]# echo $res
2


### 3 $(( ))、$[ ] 同 expr,不支持浮点数运算
[root@localhost ~]# echo $((1+1))   # 注意:两个 括号前后都不需要空格
[root@localhost ~]# echo $[1+1]
2

[root@localhost ~]# echo $((1.0+2.0))
[root@localhost ~]# echo $[1.0+2.0]
-bash: 1.0+2.0: 语法错误: 无效的算术运算符 (错误符号是 ".0+2.0"### 4 let 不支持浮点数运算,而且不支持直接输出,只能赋值
[root@localhost ~]# let res=1+1     # 同js中,let声明变量差不多
[root@localhost ~]# echo $res
2

[root@localhost ~]# let res=50/5
[root@localhost ~]# echo $res
10

[root@localhost ~]# let c=1.3*3
-bash: let: c=1.3*3: 语法错误: 无效的算术运算符 (错误符号是 ".3*3"

4 条件测试

# 测试命令: test  检查文件类型 或 比较值

# 格式: 
test  条件
[ 条件 ]       # 注意[] 与条件 有空格隔开
[[ 条件 ]]     # 常用这个           !!!
((条件))       # 涉及数字  用这个   !!! 可直接使用比较符号


# 测试结果,可通过 $? 获取,判断上一条命令执行结果是否为真
若$?值为0 表示命令执行成功 否则为失败


# 多条件比较
-a   并且
-o   或者
  eg:[ 1 -lt 2 -a 2 -lt 3 ]

&&   并且    # [[ expr1 && expr2 ]]    必须使用双括号 中括号或小括号都可以
||   或者    # (( expr1 || expr2 ))

4.1 文件状态测试

-d  目录
-s  文件长度     > 0、非空
-f  正规文件
-e  文件是否存在

-w  可写
-r  可读
-x  可执行

-L  符号连接
-u  文件有 suid 位设置

# eg
[ -d /etc/init.d ]

4.2 字符串测试

=    两个字符串相等
!=   两个字符串不相等
-z   空串
-n   非空串

=~   判断string和右边的正则表达式pattern是否匹配


[root@MiWiFi-R3-srv ~]# var1='abc';var2='123'
[root@MiWiFi-R3-srv ~]# [ $var1 = $var2 ]
[root@MiWiFi-R3-srv ~]# echo $?
1

[root@hecs-147737 ~]# var2=''
[root@hecs-147737 ~]# [ -z $var2]
[root@hecs-147737 ~]# echo $?
0

4.3 数值测试

# 1 []  或 [[]]   不能使用具体的符号,不然结果报错或者错误
-eq  等于
-ne  不等于

-gt  大于
-ge  大于等于

-lt  小于
-le  小于等于

[root@MiWiFi-R3-srv ~]# [ 10000 -gt 250 ] 
[root@MiWiFi-R3-srv ~]# echo $?
0


# 2 或者使用 (())  就可以使用符号
(( 10 > 20 ))
echo $?
1

5 流程控制

5.1 分支结构

5.1.1 if判断

### 单分支
if 条件; then
    代码
fi


### 双分支
if 条件; then
    代码1
else
    代码2
fi


### 多分支
if 条件; then
    代码1
elif 条件; then
    代码2
elif 条件; then
    代码3
else
    代码4
fi
# 分支案例
#!/bin/bash
username='egon'
password='123'
read -p 'user: ' name      # read 用于从标准输入读取数值  -p 后面跟提示信息,即在输入前打印提示信息
read -p 'passwd: ' passwd

# if [ $name = $username -a $passwd = $password ]; then
if [[ $name = $username && $passwd = $password ]]; then
    echo 'login successful'
else
    echo 'username or password error'
fi

5.1.2 case条件

# 格式
case 变量 in 
    变量值1)
        命令序列
        ;;
    变量值2)
        命令序列
        ;;
    变量值n)
        命令序列
        ;;
    *)              # 变量 都不在变量值中,走该命令序列
        命令序列   
        exit    
esac
### case 案例
安装不同php的版本
 1.菜单,PHP版本的菜单
 2.提示用户根据菜单进行选择安装不同的php
 3.根据用户选择进行安装不同的php版本



### 案例脚本:case-1.sh
vim case-1.sh

#!/bin/bash

# 1.准备PHP的版本菜单
cat << EOF
#######################################
1.安装PHP-5.5版本
2.安装PHP-5.6版本
3.安装PHP-7.1版本
4.退出当前安装
#######################################
EOF

# 2.提示用户根据菜单进行选择安装不同的PHP版本
read -p "请根据的上方的菜单,进行选择安装不同的PHP版本[1|2|3|4]:" Install
# 3.根据用户的选择,进行安装不同的版本
case $Install in
    1)
        echo "你选择了安装PHP-5.5版本.............."
        echo "正在安装PHP-5.5版本,请稍后..........."
        sleep 3
        echo "PHP-5.5版本安装成功................."
        ;;
    2)
        echo "你选择了安装PHP-5.6版本.............."
        echo "正在安装PHP-5.6版本,请稍后..........."
        sleep 3
        echo "PHP-5.6版本安装成功.................."
        ;;
    3)
        echo "你选择了安装PHP-7.1版本..............."
        echo "正在安装PHP-7.1版本,请稍后............"
        sleep 3
        echo "PHP-7.1版本安装成功..................."
        ;;
    4)
        echo "你没有选择安装任何PHP版本!程序退出!"
        exit
        ;;
    *)
        echo "你的选择不符合要求!请根据上方菜单进行选择[1|2|3|4]."
        exit
esac

5.1.3 向脚本传递参数

# 系统预定义变量
$数字  :  获取传递给脚本 具体位置的参数  # 0 是获取自己脚本文件名
$*     : 传递给脚本的  所有的参数
$@     : 传递给脚本的  所有的参数
$$     : 当前脚本的进程PID
$#     : 当前脚本的参数个数
$?     : 当前脚本 进程执行的状态  # 0表示真


### eg: test.sh
echo $0
echo $1
echo $2
echo $3
echo ${11}
echo '$$' $$
echo '$*' $*
echo '$@' $@
echo '$#' $#
echo '$?' $?


# 测试:
python test.sh 1 2 3 4 5 6 7 8 9 10 11

# 输出结果:
./test.sh
1
2
3
11
$$ 14312
$* 1 2 3 4 5 6 7 8 9 10 11
$@ 1 2 3 4 5 6 7 8 9 10 11
$# 11
$? 0

5.2 循环结构

### 退出循环
exit       # 退出循环,退出脚本
continue   # 默认退出本次循环
             忽略本次循环剩余的代码,直接执行下一次循环
             
break      # 默认退出本层循环
             结束当前循环,继续执行本层循环 外面的

5.2.1 while循环

### 1 条件判断循环
while 条件测试
do
    循环体
done


### 2 无限循环
while true
do
    循环体
done


### 3 读入文件 循环
while read line
do
    循环体
done < file
### while循环案例
1.while循环读入文件的方式,进行创建用户
2.并为其设置一个24位的随机密码,要求密码由数字、大小写字母、特殊符号组成


### 创建随机密码命令
yum install -y expect  # 安装

mkpasswd 
    -l   # 设置密码的长度
    -d   # 字符的最少个数
    -c   # 小写字母的最少个数
    -C   # 大写字母的最少个数
    -s   # 特殊符号的最少个数

mkpasswd -l 24 -d 6 -c 6 -C 6 -s 6



### 案例脚本:while-1.sh
vim while-1.sh

# 根据user.txt 创建用户脚本,并设置24位初始密码
#!/bin/bash

while read line
do
    id $line &>/dev/null
    if [ $? -eq 0 ]; then
        echo "用户${line}已经存在"
    else
        Pass=$(mkpasswd -l 24 -d 6 -c 6 -C 6 -s 6)
        useradd $line &>/dev/null && echo $Pass | passwd --stdin $line  &>/dev/null
        if [ $? -eq 0 ];then
            echo "用户[$line]创建成功!初始密码设置成功!密码文件为pass.txt"
            echo -e "User: $line \tPass: $Pass" >> pass.txt && chmod 400 pass.txt
        else
            echo "用户[$line]创建失败!"
        fi
    fi
done < user.txt

5.2.2 for循环

# 格式
for i in 1 2 3     # 被循环的是 以空格分开的内容
do
    循环体
done

# shell的for循环 常用in列表方式

# eg:
for i in 1 2 3
for i in {1,2,3}
for i in `seq 0 3`    # seq 0 3 间隔步长   # 获取从0到3,间隔多少的整数

for i in {1..9}
for i in {9..1}
for i in {a..z}
for i in {A..Z}
for i in {X..Z}

for i in $(cmd)
for i in $(find ...)
### for循环案例
1.从文件中 user.txt进行取值
2.先把用户调用出来,判断用户是否存在
3.存在则提示已经存在,不要再去设置密码
4.不存在则进行创建,再创建密码
5.显示创建结果


### user.txt
vim user.txt

aaa:thasdj
bbb:dsasda
ccc:asdads
ddd:sadads



### 案例脚本1:for-1.sh
vim for-1.sh

#!/bin/bash
for i in $(cat user.txt)
do
    # 1.把用户和密码分开
    User=$(echo $i | awk -F: '{print $1}')
    Pass=$(echo $i | awk -F: '{print $2}')
    # 2.判断用户是否存在
    id $User &>/dev/null
    if [ $? -eq 0 ];then
        echo "用户[$User]已经存在!"
    else
        useradd $User &>/dev/null && echo $Pass | passwd --stdin $User &>/dev/null
        if [ $? -eq 0 ];then
            echo "用户[$User]创建成功! 并且密码初始化成功!"
        else
            echo "用户[$User]创建失败!"
        fi
    fi
done


### 案例脚本2:for-2.sh
vim for-2.sh

for i in $(cat user.txt)
do 
    User=$(echo $i | awk -F: '{print $1}')
    userdel -r $User && echo "$User delete successful"
done

6 函数

### 1 定义函数
# 第一种
函数名() {
    命令集合
}


# 第二种
function 函数名 {
    命令集合
}



### 2 调用函数
函数名



### 3 参数传递     跟给脚本文件传递一样   按按位置传递

# eg: function-1.sh
#!/bin/bash
# hellofun
function hello(){
    echo "Hello! The first parameter is '$1'."
}

hello good

# 该脚本执行的结果是: 
Hello! The first parameter is 'good'.



### 4 函数状态返回
# 返回值 在shell中有两种方式
echo     # 返回数据,自定义的输出数据    打印字符串

return   # 命令的执行结果 返回值  0-255之间的正整数
           $? 来查看  0默认表示成功,非0表示失败

# eg: function-2.sh
#!/bin/bash

fun() {
    echo "123"
    return 1
}
Test=$(fun)   # 调用函数,并赋值给变量Test
echo "函数的状态返回码是:$?"
echo "函数的返回数据是:$Test"

# 结果
函数的状态返回码是:1
函数的返回数据是:123



### 5 函数载入和删除
set   # 查看已载入的函数
unset function-name # 取消载入

7 数组

7.1 数组介绍

### 1 什么是数组
简单讲:数组就是变量的一种,变量只能存储一个值,而数组可以存储多个值


### 2 数组分类
普通数组  # 只能使用 正整数 作为数组索引    # 类似 python 列表

关联数组  # 可以使用 字符串 作为数组索引    # 类似 python 字典

7.2 基本使用

7.2.1 定义数组

### 1 定义普通数组

# 方式1:按索引位置,依次直接赋值
数组名[索引号]=字符串

# eg:
shuzu_1[0]=nginx
shuzu_1[1]=python
shuzu_1[2]=mysql


# 方式2:一次性定义多个值
shuzu_2=(oldboy oldgirl oldteacher)


# 方式3:根据命令的结果,一次性定义     默认以空白字符为分隔符
shuzu_3=($(cat user.txt))


# 方式4:自定义索引序列号
shuzu_4=(beijing shanghai [5]=wuhan [10]=nanjing)



### 2 定义关联数组
# 需要先声明
declare -A 数组名   # 声明关联数组


# 方式1:按索引位置,依次直接赋值
declare -A site
site["google"]="www.google.com"
site["runoob"]="www.runoob.com"
site["taobao"]="www.taobao.com"



# 方式2:一次性定义多个值

# eg:
declare -A site=(["google"]="www.google.com" ["runoob"]="www.runoob.com" ["taobao"]="www.taobao.com")

7.2.2 访问数组

# 1 查看已定义的所有 普通数组 
declare -a

# 2 查看已定义的所有 关联数组
declare -A


# 3 查看数组 所有的值    * 和 @ 符号都可以
echo ${array[*]}

# 4 查看数组 某个索引对应的值
echo ${array[索引]}

# 5 查看数组 值的个数
echo ${#array[*]}

# 6  查看数组 所有的索引
echo ${!array[*]}

7.3 数组循环与遍历

# 案例脚本:统计Tcp连接

vim tcp11.sh

#!/bin/bash
# 1.声明关联数组
declare -A Tcp_state

# 2.定义变量   普通数组
Status=$(netstat -ant | awk 'NR>2{print $NF}')

# 3.循环賦值
for i in $Status      # 根据 数组值 循环
do
    let Tcp_state[$i]++   # 自增
done
# 4.索引遍历
for i in ${!Tcp_state[*]}  # 根据 所以 遍历
do
    echo -e "当前Tcp状态为:$i\t\t出现的次数为:${Tcp_state[$i]}"
done


# 结果:
当前Tcp状态为:ESTABLISHED        出现的次数为:3
当前Tcp状态为:LISTEN        出现的次数为:5
posted @   Edmond辉仔  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示