第8章:shell编程:bash特殊符号、常用快捷键、输入输出重定向;变量、字符串操作、数组;read命令;shell运算符let/expr/bc/$(())/$[];流程控制if/case/for/while/until/select/trap

 

复制代码
shell脚本格式;调试shell脚本;()与{}的区别;bash退出状态
=============================================================  
shell脚本基础
格式要求:首行shebang机制
    #!/bin/bash
    #!/usr/bin/python
 
    #号是shexxx,!是bang
 
注释以#开头
 
----------------------------------------------------------------------------------------------
运行脚本的方式:
    1.给予脚本执行权限,在命令行上以绝对路径运行脚本
    2.给予脚本执行权限,在命令行上以相对路径运行脚本(此处相对路径是./xxx的形式)
    3.类似命令的方式来运行脚本,将脚本文件放在$PATH中的目录下,建议放在家目录的bin目录下,例如root用户的~/bin
    4.使用解释器运行脚本,例如 bash aaa.sh;python bbb.py
    5.  [root@yefeng28325 ~]# echo "echo hello world" |bash  
        [root@yefeng28325 ~]# cat hello.sh |bash
        [root@yefeng28325 ~]# curl http://192.168.0.1/hello.sh |bash
 
 
脚本调试
    bash -n systeminfo.sh               #检查脚本的语法错误
    bash -n /root/bin/systeminfo.sh     #检查脚本的语法错误
    bash -x systeminfo.sh               #调试执行
    bash -x /root/bin/systeminfo.sh     #调试执行
 
---------------------------------------------------------------------------------------------
语法错误才会导致程序终止;非语法错误不影响后续的执行
 
脚本规范
#!/bin/bash                                 第一个一般为调用使用的语言
#########################################################################
# File Name: test.sh                        程序名,避免更改文件名而无法找到正确的文件
# Revision: 1.1                             版本号
# Date: 2020/06/01                          最后更改时间
# Author: yefeng                            作者
# mail: 10086@qq.com                        
# Description: xxxxxxx                      功能描述,注意事项
#########################################################################
# xxxxxxx: xxxxxxx                          各个版本的更新简要说明
 

----------------------------------------------------------
()与{}的区别
1.()小括号内的命令是运行在子shell进程中的;{}大括号则是在当前进程中运行的
 
在man bash中可以查找相关说明,”/\(list\)”查找小括号的说明;”/\{ list; \}”查找大括号的说明
小括号启用的子进程是可以拿到当前进程的局部变量;这一点和一般的子进程不一样!(比如通过bash开启一个子进程)
----------------------------------------------------------
退出状态
进程使用退出状态来报告成功or失败
0表示成功,1-255表示失败
变量$?保存了最近一个命令的退出状态(查看了菜鸟教程,$?也会保存函数的返回值嘛,即函数return的内容)
 
bash中自定义退出状态码:exit [n]
注意:脚本中一旦遇到exit命令,脚本会立即终止;退出状态取决于exit命令后面的数字
注意2:如果未给脚本指定退出状态码,那么退出状态码默认为脚本中执行的最后一条命令的状态码
shell脚本格式;调试shell脚本;()与{}的区别;bash退出状态
复制代码

 

复制代码
shell中特殊符号'' "" `` $() () {} [ ]    #  $ \
''            单引号。在单引号中所有的特殊符号,如“$”和”(反引号)都没有特殊含义。单引号括起来的都是普通字符,会原样输出
""          双引号。在双引号中特殊符号都没有特殊含义,但是“$”,"`"(esc键下面)和“\”是例外,拥有“调用变量的值”、“引用命令”和“转义符”的特殊含义。
``          反引号。反引号括起来的内容是系统命令,在Bash中会先执行它。和( ) 作 用 一 样 , 不 过 推 荐 使 用 ()作用一样,不过推荐使用()作用一样,不过推荐使用(),因为反引号非常容易看错。
$()            和反引号作用一样,用来引用系统命令。(推荐使用)
()            用于一串命令执行时,()中的命令会在子Shell中运行
{}            用于一串命令执行时,{ }中的命令会在当前Shell中执行。也可以用于变量变形与替换。
[ ]            用于变量的测试。
#            在Shell脚本中,#开头的行代表注释。
$            用于调用变量的值,如需要调用变量name的值时,需要用$name的方式得到变量的值。
\            转义符,跟在\之后的特殊符号将失去特殊含义,变为普通字符。如$将输出“$”符号,而不当做是变量引用。
shell中特殊符号'' "" `` $() () {} [ ] # $ \
复制代码

 

复制代码
小知识点
=======================================
[root@yefeng28325 ~]# name="echo"
[root@yefeng28325 ~]# $name "echo2"
echo2
[root@yefeng28325 ~]# echo $name   这两者的差异需要细细体会
echo
 
 
[[]]双层中括号的说明可以在man bash中找到,/\[\[
    使用== != 匹配的是通配符
    而=~时,匹配的却是扩展正则
 
 

别名默认是不能放在脚本里运行的,即脚本里的alias是无效的
小知识点
复制代码

 

Bash 常用快捷键---第1章:linux基础
输入输出重定向---第1章:linux基础

变量

复制代码
用户环境变量文件/etc/profile、/etc/profile.d/*.sh、/etc/bashrc、~/.bash_profile,~/.bashrc;shell的两种登陆方式;bash退出任务 ~/.bash_logout文件;变量$-
---------------------------------------------------------------------------------
配置用户环境
bash的配置文件
按影响范围分类
    1.全局配置:/etc/profile,/etc/profile.d/*.sh,/etc/bashrc
    2.个人配置:~/.bash_profile,~/.bashrc
        (bashrc  bash run command)
按功能分类:
    1.profile类:/etc/profile,/etc/profile.d/*.sh,~/.bash_profile
        (1)用户定义环境变量;(2)运行命令or脚本
    2.bashrc类:/etc/bashrc,~/.bashrc
        (1)定义别名and函数;(2)定义本地变量
 
全局环境变量建议存放在/etc/profile.d/*.sh
个人环境变量建议存放在~/.bash_profile
全局别名and函数建议放在/etc/bashrc
个人别名and函数建议放在~/.bashrc
 
 
修改profile和bashrc文件后,生效的两种方法:
    1.重启shell进程
    2.source或. 重新加载文件
        source是在当前shell进程运行,而不是开启一个子进程运行脚本,所以source可以重新加载配置
        反过来,我们一般不用source去运行脚本,因为可能会导致环境变量被修改!
 
---------------------------------------------------------------------------------
shell两种登陆方式(加载配置的顺序和种类都不同)
1.交互式登陆
    (1)直接通过终端输入密码登陆
    (2)su - 切换用户
    执行配置顺序:/etc/profile--->/etc/profile.d/*.sh--->~/.bash_profile--->~/.bashrc--->/etc/bashrc
2.非交互式登陆
    (1)su 切换用户(不携带"-")
    (2)图形界面打开的终端
    (3)执行脚本
    (4)任何其他的bash实例
    执行配置顺序:~/.bashrc--->/etc/bashrc--->/etc/profile.d/*.sh
 
---------------------------------------------------------------------------------
bash退出任务
~/.bash_logout文件:在退出登录shell是运行
    1.创建自动备份
    2.清除临时文件
 
---------------------------------------------------------------------------------
变量$-
[root@yefeng28325 ~]# echo $-
himBH
h:hashall,打开这个选项后,shell会将命令所在的路径hash下来,避免每次都要查询。通过set +h将h选项关闭
i:interactive-comments,包含这个选项说明当前的shell是一个交互式的shell;非交互登陆时,i选项是关闭的,例如脚本中
m:monitor,打开监控模式,就可以通过job control来控制进程的停止、继续,后台or前台执行等
B:braceexpand,大括号扩展
H:history,H选项打开,可以展开历史列表中的命令,可以通过!感叹号来完成,例如"!!"最近的一个历史命令,"!n"第n个历史命令
 
用户环境变量文件/etc/profile、/etc/profile.d/*.sh、/etc/bashrc、~/.bash_profile,~/.bashrc;shell的两种登陆方式;bash退出任务 ~/.bash_logout文件;变量$-
复制代码
复制代码
profile、bashrc调用顺序的研究


[root@yefeng28325 ~]# cat /etc/profile
# /etc/profile
……………………………………………………
for i in /etc/profile.d/*.sh /etc/profile.d/sh.local ; do   #在/etc/profile快结束时,就去调用/etc/profile.d/*.sh了
    if [ -r "$i" ]; then
        if [ "${-#*i}" != "$-" ]; then 
            . "$i"
        else
            . "$i" >/dev/null
        fi
    fi
done
 
unset i
unset -f pathmunge
 
====================================================================
[root@yefeng28325 ~]# cat .bash_profile 
# .bash_profile
 
# Get the aliases and functions
if [ -f ~/.bashrc ]; then                                                #在运行.bash_profile时,会去调用~/.bashrc
        . ~/.bashrc
fi
 
# User specific environment and startup programs
 
PATH=$PATH:$HOME/bin
 
export PATH
[root@yefeng28325 ~]# 
 
====================================================================
[root@yefeng28325 ~]# cat .bashrc 
# .bashrc
 
# User specific aliases and functions
 
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
 
# Source global definitions
if [ -f /etc/bashrc ]; then                   #在运行~/.bashrc时,会去调用/etc/bashrc
        . /etc/bashrc
fi
 
====================================================================
 
[root@yefeng28325 ~]# cat /etc/bashrc   #这个是最后执行的
profile、bashrc调用顺序的研究
复制代码

 

 

复制代码
变量;局部变量;环境变量;只读变量和位置变量;变量引用
=================================================
变量
bash的变量是弱类型;java,c#等语言的变量为强类型
弱类型变量:语言在运行时会隐式做数据类型转换;无需指定类型,默认为字符型;参与运算会自动进行隐式类型转换;变量无需事先定义可直接调用。
强类型变量:使用变量必须先声明变量,且指定变量类型;不允许隐式的类型转换;参与运算必须符合类型要求;可以进行强制类型转换。
 
变量命名法则:
    1.不能使用系统保留的关键字,例如if、for
    2.只能使用属组、字母、下划线,且不能以数字开头
    3.见名知义
    4.建议使用驼峰命名法(命名法可以参考各种系统文件or模块;java类名使用大驼峰,方法使用小驼峰;python则建议使用下划线)
 
bash中的变量
根据变量的生效范围划分
    1.局部变量:生效范围为当前shell进程;对当前shell之外的其他shell进程,包括当前shell的子shell进程均无效
    2.环境变量(全局变量):生效范围为全局
    3.本地变量:生效范围为当前shell进程中某代码片段,通常指函数
    4.位置变量:$1,$2...(注意$(10)),运行脚本时传递的参数
    5.特殊变量:$?,$0,$*,$@,$#,$$
        $$:当前进程的进程ID
        $PPID:当前进程的的父进程ID
----------------------------------------------------------
局部变量
变量赋值:
    name='value' #将字符串赋值给变量(注意:赋值时,=号前后无空格)
    name1=$name  #将某变量的值赋值给变量
    name2=`COMMAND` name3=$(COMMAND) #将命令的结果赋值给变量
----------------------------------------------------------
环境变量
环境变量声明、赋值:
    export name=value
    declare -x name=value
显示所有环境变量:
    env;printenv;export;declare -x; #可能存在些许不同
bash内建的环境变量:
    PATH
    SHELL
    USER
    UID
    HOME
    PWD
    SHLVL       #嵌套深度
    LANG
    MAIL
    HOSTNAME
    HISTSIZE
    _ 下划线
    -

环境变量大多数在/etc/profile文件中初始化,该文件在用户登录时候执行
所有的shell环境变量都会被传递给shell的子程序

    $HOME       读写        当前用户的主目录
    $PATH       读写        以冒号分割的,用来搜索命令的目录列表
    $PS1        读写        命令提示符,通常是$符号,但在bash中,你可以使用一些更复杂的值。
    $PS2        读写        二级提示符,用来提示后续的输入,通常是>字符
    $IFS        读写        输入域分隔符当shell读取输入时,用来分隔单词的一组字符,他们通常是空格、制表符和换行符
    $0          只读        shell脚本的文件名字
    $1-9        只读        命令行参数1-9的值,参数10之后建议使用${10}这种形式
    $*          只读        这个变量代表命令行中所有的参数,$把所有的参数看成一个整体
    $@          只读        这个变量也代表命令行中所有的参数,不过$@把每个参数区分对待
        实测:当$@与$*都用""包括时,即"$*""$@""$*"是字符串,"$@"是数组
    $#          只读        命令行参数的总个数
    $$          只读        shell脚本进程的ID号
    $?          只读        最近一次命令的退出状态
    $!          只读        最近一次后台进程的ID号
----------------------------------------------------------
只读变量和位置变量
声明只读变量:
    readonly name
    declare -r name
查看只读变量:
    readonly -p
 
位置变量:在运行脚本时,传递给脚本的参数
    $1,$2...$(10);对应第1、第2、第10个参数,shift[n]换位置
    $0  命令本身,即脚本名称
    $*  传递给脚本的所有参数,全部参数合为一个字符串
    $@  传递给脚本的所有参数,每个参数为独立字符串
        $*$@ 只有在双引号包起来的时候才会有差异
    $#  传递给脚本的参数个数
    set --  #清空所有位置变量
 
    shift  移除指定个数的位置变量,默认移除第一个变量
        用法:shift;shift 2
 
 
----------------------------------------------------------
变量引用
    ${name} $name #引用变量时建议使用"$"符号;"$"不是必须的,只要命令可以识别出变量,那么就可以不加"$"(这个需要经验的积累,才知道哪些命令可以不需要加"$")
    ""  #弱引用,bash将会解析""中的变量(在双引号中的变量仍然是需要加"$"的)
    ''  #强引用,所见即所得
显示已定义的所有变量:set
删除变量:unset name
变量;局部变量;环境变量;只读变量和位置变量;变量引用
复制代码
复制代码
变量相关命令的比较:declare、unset、set、env、export、readonly


==============================================================================
变量相关命令的比较:declare、unset、set、env、export、readonly

declare命令用于声明和显示已存在的shell变量。当不提供变量名参数时显示所有shell变量。declare命令若不带任何参数选项,则会显示所有shell变量及其值。declare的功能与typeset命令的功能是相同的。
unset命令用于删除已定义的shell变量(包括环境变量)和shell函数。unset命令不能够删除具有只读属性的shell变量和环境变量。
set命令作用主要是显示系统中已经存在的shell变量,以及设置shell变量的新变量值。使用set更改shell特性时,符号"+""-"的作用分别是打开和关闭指定的模式。set命令不能够定义新的shell变量。如果要定义新的变量,可以使用declare命令
env命令用于显示系统中已存在的环境变量,以及在定义的环境中执行指令。该命令只使用"-"作为参数选项时,隐藏了选项"-i"的功能。若没有设置任何选项和参数时,则直接显示当前的环境变量。
    set命令可以查看所有变量,而env命令只能查看环境变量。
export命令用于将shell变量输出为环境变量,或者将shell函数输出为环境变量。
readonly命令用于定义只读shell变量和shell函数。readonly命令的选项-p可以输出显示系统中所有定义的只读变量。


https://www.cnblogs.com/qlqwjy/p/9075766.html
总结:
  1.declare var=value   可以声明一个shell变量(与之等价的是 var=value,typeset var=value),也可以直接用declare -x var=value 声明一个变量并直接输出到环境变量,也可以加上-r参数表示只读变量。
  2.unset var 可以删除变量,包括shell变量和环境变量(当前用户变量),不能够删除具有只读属性的shell变量和环境变量。
  3.set -a var  可以将var变量输出到环境变量
  4.env可以查看所有的环境变量,可以加管道命令与grep命令过滤变量
   5.export var=value用于定义一个环境变量,修改环境变量也是这个,等价于 declare -x var=name
   export -n var 用于从环境变量删除此变量,但是shell变量中此变量仍然存在。
变量相关命令的比较:declare、unset、set、env、export、readonly
复制代码
复制代码
变量赋值操作;字符串切片、处理
=========================================================================
变量赋值操作

变量配置方式            var未初始化     var为空字符串       var为非空字符串
$variable                  空           $variable            $variable
${variable}                空           $variable            $variable
${variable-string}       string         空                   $variable
${variable:-string}      string         string               $variable
${variable+string}         空           string                string
${variable:+string}        空           空                    string

${variable=string}         功能同"-",区别在于当var未初始化未初始化时,variable会被初始化为string
${variable:=string}        功能同":-",区别在于当var未初始化未初始化or var为空时,variable会被初始化为string

${variable?string}      expr输出到stderr     空                  $variable
${variable:?string}     expr输出到stderr    expr输出到stderr     $variable

========================================================================
字符串切片、处理
${#var}                 #返回var的长度
${#var:offset}          #字符串切片,取自偏移量offset到结束的所有字符
${#var:offset:number}  #字符串切片,取自偏移量offset之后的number个数字符
${#var: -length}        #字符串切片,注意有空格,去倒数的指定length位数到结尾的字符
${#var: -length:offset} #字符串切片,注意有空格,去倒数的指定length位数字符,然后再指定偏移量offset

字符串处理
本质上#%这2个符号才是模式的关键,*只是用来进行匹配的。。。

${var#*word}           #使用word在字符串var中从左向右进行匹配;若未匹配,则显示字符串var;若有匹配,则只显示匹配后的所有字符
${var##*word}          #功能基本同上,但该模式为贪婪模式;即上面的方式,只会进行一次匹配;而这种方式则会进行多次匹配,以最后一次匹配为准
${var%word*}           #从右向左匹配,功能同上
${var%%word*}          #从右向左匹配,贪婪模式

查找并替换
${var/word1/word2}      #查找word1,替换为word2;仅替换第一个匹配到的
${var//word1/word2}     #查找替换,替换所有
${var/#word1/word2}     #仅匹配行首
${var/%word1/word2}     #仅匹配行尾
查找并删除
${var/word1}            #查找并删除,其实就是查找替换的缩减版本
${var//word1} 
${var/#word1} 
${var/%word1} 
字符串转换大小写
${var^^}                #所有字母大写
${var,,}                #所有字符小写
变量赋值操作;字符串切片、处理
复制代码

 

复制代码
declare、typeset;删除变量unset;set命令
===================================================
https://www.cnblogs.com/klb561/p/9236468.html
typeset现在已经基本不用了,就用declare
declare也可以申明环境变量,只是用export更多而已
declare命令(别名typeset)属shell内建命令,用于申明shell变量并设置变量属性,或查看已定义的shell变量和函数。若不加上任何参数,则会显示全部的shell变量与函数(与执行set指令的效果相同)。

declare命令用于声明 shell 变量
    +/-  "-"可用来指定变量的属性,"+"则是取消变量所设的属性。
    -i  设置变量为整数,[设置值]可以是数值,字符串或运算式。
    -a   设置变量为数组array
    -r   设置变量为只读
    -f   如果后面没有参数的话会列出之前脚本定义的所有函数,如果有参数的话列出以参数命名的函数
    -x  指定的变量会成为环境变量,可供shell以外的程序来使用。

    -a:申明数组变量 -A:申明关联数组,可以使用字符串作为数组索引 -f:仅显示已定义的函数 -F:不显示函数定义 -i:声明整型变量 -l:将变量值的小写字母变为小写 -u:变量值的大写字母变为大写 -r:设置只读属性 -t:设置变量跟踪属性,用于跟踪函数进行调试,对于变量没有特殊意义 -x:将指定的shell变量换成环境变量 -p:显示变量定义的方式和值 +:取消变量属性,但是+a和+r无效,无法删除数组和只读属性,可以使用unset删除数组,但是unset不能删除只读变量

    第一种语法中可用来声明变量并设置变量的属性([rix]即为变量的属性),
    第二种语法中可用来显示shell函数。若不加上任何参数,则会显示全部的shell变量与函数(与执行set指令的效果相同)。
    declare -x  #查看所有环境变量
    declare -x PATHXXX=112233 #声明环境变量
    declare -i AA #声明整数型变量;当AA赋值为数字时,AA的值相应的数字;当AA赋值为字符串时,AA的值为0
    declare +i AA #取消变量属性;此时AA可以被赋值为字符串了
    declare -r  #查看所有只读变量
    declare -r ab #设置变量为只读
    declare -a cd='([0]="a" [1]="b" [2]="c")' #声明数组变量;echo ${cd[1]};echo ${cd[@]} //显示整个数组变量内容   
    
-------------------------------------------------------
删除变量
可以使用unset命令,或者显式地将值置为null也可(null是所有未声明的变量的状态)
unset为shell内建指令,可删除变量或函数。
    -f  仅删除函数。
    -v  仅删除变量。
unset 变量名
-------------------------------------------------------
set指令能设置所使用shell的执行方式,可依照不同的需求来做设置。
    -v 显示shell所读取的输入值。
    +参数  取消某个set曾启动的参数。
declare、typeset;删除变量unset;set命令
复制代码

 

复制代码
间接变量引用
如果第一个变量的值是第二个变量的名字,那么从第一个变量直接引用第二个变量的值成为间接变量应用
即var1的值为“var2”;“var2”既为“var1”的值,同时又为变量名
bash shell提供了两种格式实现间接变量引用
    1.${!var1}
    2.eval var3=\$$var1;echo $var3
间接变量引用
复制代码

 

复制代码
数组
================================================================
数组:存储多个元素的连续的内存空间,相当于多个变量的集合
数组名和索引
    索引:编号从0开始,属于数值索引
    注意:索引可支持使用自定义的格式,而不仅是数值格式,即为关联索引,bash4.0版本才开始支持该特性
    bash的属组支持稀疏格式(索引不连续)
声明数组
    declare -a ARRAY_NAME
    declare -A ARRAY_NAME(关联数组)
        关联属组,即属组的索引不是0123..;而是自定义的格式,例如0/a/b/6/7/8这种不规则的
    注意:两者不可相互切换

================================================================
数组赋值
1.一次赋值一个元素
    ARRAY_NAME[INDEX]=VALUE
        示例:weekdays[0]="sunady"
2.一次性赋值所有元素
    ARRAY_NAME=("VAL1" "VAL2" "VAL3")
3.只赋值特定元素
    ARRAY_NAME=([0]="VAL1" [3]"VAL3")
4.交互式数组值对赋值
    read -a ARRAY

显示所有数组:declare -a

================================================================
引用数组
1.引用数组元素
    ${ARRAY_NAME[INDEX]}
        注意:[INDEX]可省略,省略即引用下标为0的元素
2.引用数组所有元素
    ${ARRAY_NAME[*]}
    ${ARRAY_NAME[@]}
3.数组的长度
    ${#ARRAY_NAME[*]}
    ${#ARRAY_NAME[@]}
4.删除数组中的某元素:导致稀疏格式
    unset ARRAY_NAME[INDEX]
5.删除整个数组
    unset ARRAY_NAME


使用实例
    貌似也不需要显示地声明一个属组,可以直接对一个数组进行赋值的。。
    但是关联数组一定要先声明
    array1=(`ls`)
    array2=( /boot/* )
    number=({1..10})


================================================================
数组数据处理

引用数组中的元素
数组切片:${ARRAY[@]:offset:number}
    offset:要跳过的元素个数
    number:要去除的元素个数
    去偏移量之后的所有元素${ARRAY[@]:offset}
向属组中追加元素:
    ARRAY[${#ARRAY[*]}]=value
关联数组:
    declare -A ARRAY_NAME
    ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2' [idx_name3]='val3')
    注意:关联数组必须先声明再调用
数组
复制代码

 

shell变量默认是字符串型

命令替换、用户输入read

复制代码
命令替换:$(command)或`command`这两种表达式,shell会执行该命令,并替换为该命令的输出。
====================================================================
用户输入read
选项:
    -a 后跟一个变量,该变量会被认为是个数组,然后给其赋值,默认是以空格为分割符。
    -p: “提示信息”:在等待read输入时,输出提示信息
    -t: 秒数:read命令会一直等待用户输入,使用此选项可以指定等待时间
    -n: 数字:read命令只接受指定的字符数,就会执行
    -s: 隐藏输入的数据,适用于机密信息的输入
    -d: 后面跟一个标志符,其实只有其后的第一个字符有用,作为结束的标志。
    -e: 在输入的时候可以使用命令补全功能。
变量名:
    变量名可以自定义,如果不指定变量名,会把输入保存入默认变量REPLY.
    如果只提供了一个变量名,则整个输入行赋予该变量.
    如果提供了一个以上的变量名,则输入行分为若干字,一个接一个地赋予各个变量,而命令行上的最后一个变量取得剩余的所有字

用法示例:
    read -p "输入网站名:" website
    echo "你输入的网站名是 $website" 
    read        #默认保存到默认变量REPLY
命令替换、用户输入read
复制代码

 shell 运算符

复制代码
算数运算符;bash中的6种数学运算:let命令、expr命令、bc命令、$(())、$[];逻辑运算
============================================================================
原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。
expr 是一款表达式计算工具,使用它能完成表达式的求值操作。

运算符    说明                                        举例
+        加法                                        expr $a + $b 结果为 30-        减法                                        expr $a - $b 结果为 -10*        乘法                                        expr $a \* $b 结果为 200/        除法                                        expr $b / $a 结果为 2%        取余                                        expr $b % $a 结果为 0。
=        赋值                                        a=$b 将把变量 b 的值赋给 a。
==    相等。用于比较两个数字,相同则返回 true(真)。     [ $a == $b ] 返回 false(假)。
!=    不相等。用于比较两个数字,不相同则返回 true。        [ $a != $b ] 返回 true。
**(乘方)
其他算术运算符: += -= *= /= %= ++ --

----------------------------------------------------------
实现算术运算
    1.let var=算术表达式
    2.var=$[算术表达式]
    3.var=$((算术表达式))
    4.var=$(expr arg1 arg2 arg3 ...)
    5.declare -i var=数值;var+=1
    6.echo '算术表达式' |bc
注意:乘法符号在某些场景中需要转义,如*
bash内建的随机数生成器:$RANDOM(0-32767)
echo $[$RANDOM%50]  0-49之间的随机数
 
bash中的4种数学运算:let命令、expr命令、bc命令
let命令:let num+=10
用expr做计算的时候,运算符要与变量或数值间要有空格分开。
bc支持数学运算:echo 512*63*2610*255|bc

----------------------------------------------------------
逻辑运算
! 非
& and
| or   
&& 短路与(第一条命令判断结果为假,则跳过第二条命令的执行)
|| 短路或(第一条命令判断结果为真,则跳过第二条命令的执行)
    短路判断其实就是判断第一个命名的执行结果,即判断$?
 
^ 异或(我的理解:异或是基于二进制进行判断的,任意字符串在进行异或时,先转变为二进制;然后进行同位比较;相同为假,不同为真,即0^0=0,1^1=0,1^0=1,0^1=1)
    例如echo $((1^8))的结果为9;1转为二进制0001,8转为二进制1000,同位异或结果为1001,即9
echo $a $b;a=$((a^b));b=$((a^b));a=$((a^b));echo $a $b  #实现数值交换
5 7
7 5
算数运算符;bash中的6种数学运算:let命令、expr命令、bc命令、$(())、$[];逻辑运算
复制代码

 

复制代码
test 和 [ 命令、关系运算符、逻辑运算符、字符串运算符、文件测试运算符(重点)
==============================================================================================================
虽然 Linux 和 UNIX 的每个版本中都包含 test 命令,但该命令有一个更常用的别名 — 左方括号:[。test 及其别名通常都可以在 /usr/bin 或 /bin (取决于操作系统版本和供应商)中找到。
当您使用左方括号而非 test 时,其后必须始终跟着一个空格、要评估的条件、一个空格和右方括号。右方括号不是任何东西的别名,而是表示所需评估参数的结束。条件两边的空格是必需的,这表示要调用 test,以区别于同样经常使用方括号的字符/模式匹配操作。
==============================================================================================================
关系运算符
运算符    单词                        说明                                                举例
-eq        equal                    检测两个数是否相等,相等返回 true。                        [ $a -eq $b ] 返回 false。
-ne        not equal                检测两个数是否相等,不相等返回 true。                    [ $a -ne $b ] 返回 true。
-gt        great than                检测左边的数是否大于右边的,如果是,则返回 true。         [ $a -gt $b ] 返回 false。
-lt        less than                检测左边的数是否小于右边的,如果是,则返回 true。         [ $a -lt $b ] 返回 true。
-ge        great than or equal        检测左边的数是否大于等于右边的,如果是,则返回 true。     [ $a -ge $b ] 返回 false。
-le        less than or equal        检测左边的数是否小于等于右边的,如果是,则返回 true。     [ $a -le $b ] 返回 true。

----------------------------------------------------------------------------------------------------------
逻辑运算符
运算符        说明                                                    举例
!        非运算,表达式为 true 则返回 false,否则返回 true。            [ ! false ] 返回 true。
-o        或(或者)运算,有一个表达式为 true 则返回 true。            [ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a        与(并且)运算,两个表达式都为 true 才返回 true。            [ $a -lt 20 -a $b -gt 100 ] 返回 false。

----------------------------------------------------------------------------------------------------------
字符串运算符
运算符        说明                                            举例
=        检测两个字符串是否相等,相等返回 true。                [ $a = $b ] 返回 false。
!=        检测两个字符串是否相等,不相等返回 true。            [ $a != $b ] 返回 true。
-z        检测字符串长度是否为0,为0返回 true。                [ -z $a ] 返回 false。
-n        检测字符串长度是否为0,不为0返回 true。                [ -n $a ] 返回 true。
str        检测字符串是否为空,不为空返回 true。                [ $a ] 返回 true。

----------------------------------------------------------------------------------------------------------
文件测试运算符(重点)
操作符            说明                                                                            举例
-b file        检测文件是否是块设备文件,如果是,则返回 true。                                    [ -b $file ] 返回 false。
-c file        检测文件是否是字符设备文件,如果是,则返回 true。                                [ -c $file ] 返回 false。
-d file        检测文件是否是目录,如果是,则返回 true。                                        [ -d $file ] 返回 false。
-f file        检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。           [ -f $file ] 返回 true。
-g file        检测文件是否设置了 SGID 位,如果是,则返回 true。                                [ -g $file ] 返回 false。
-k file        检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。                        [ -k $file ] 返回 false。
-p file        检测文件是否是有名管道,如果是,则返回 true。                                    [ -p $file ] 返回 false。
-u file        检测文件是否设置了 SUID 位,如果是,则返回 true。                                [ -u $file ] 返回 false。
-r file        检测文件是否可读,如果是,则返回 true。                                            [ -r $file ] 返回 true。
-w file        检测文件是否可写,如果是,则返回 true。                                            [ -w $file ] 返回 true。
-x file        检测文件是否可执行,如果是,则返回 true。                                        [ -x $file ] 返回 true。
-s file        检测文件是否为空(文件大小是否大于0),不为空返回 true。                          [ -s $file ] 返回 true。
-e file        检测文件(包括目录)是否存在,如果是,则返回 true。                                 [ -e $file ] 返回 true。

注意:权限几个判断,如果只有一个部分符合,则认为是有权限的。
test 和 [ 命令、关系运算符、逻辑运算符、字符串运算符、文件测试运算符(重点)
复制代码
复制代码
条件测试test,[];条件性的执行操作符;bash的组合测试条件
----------------------------------------------------------
条件测试:若真,则返回0;若假,则返回1(bash真假的值与python相反)
测试命令:(注意:EXPRESSION前后必须有空白字符,判断结果是保存在$?中的!;中括号等效于test,查看帮助可以help test,man test)
    1.test EXPRESSION
    2.[ EXPRESSION ]    #一般建议使用[],结构看起来更清晰
    3.[[ EXPRESSION ]]  #双层中括号内能识别扩展正则,一般使用“=~”时使用双层中括号
 
    例如`[ 111 = 111 ]`;echo $? 
建议"="两端的变量用双引号引起来;若变量为空,将会导致等式不成立,包语法错误
 
test命令(test等效于[])用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。
    判断文件类型、文件权限、文件特殊权限:
        -a;-b;-d;-h;-r;-w;-x;-u;-g等等
        例如:[ -a /root/bin/test.txt ] ;echo $?
    数值比较:
        -eq        等于则为真
        -ne        不等于则为真
        -gt        大于则为真
        -ge        大于等于则为真
        -lt        小于则为真
        -le        小于等于则为真
    字符串比较(字符串测试):
        = 是否等于
        > 是否大于,ascii码比较
        < 是否小于,ascii码比较
        != 是否不等于
        =~ 左侧字符串是否能被右侧的PATTERN所匹配,即“=~”左侧为被匹配字符串,“=~”右侧为扩展正则表达式
    详细情况help test,man test;注意:用于字符串比较时用到的操作数都应该使用引号
 
----------------------------------------------------------
条件性的执行操作符
根据退出状态而定,命令可以有条件地运行
EXPRESSION && COMMAND   #条件为真,则执行命令
EXPRESSION || COMMAND   #条件为假,则执行命令
拓展
EXPRESSION && COMMAND1 || COMMAND2  #条件为真,执行命令1;条件为假,执行命令2
EXPRESSION || COMMAND1 && COMMAND2  #与上相反
EXPRESSION && COMMAND1 && COMMAND2  #条件为真,执行命令1、命令2
 
 
----------------------------------------------------------
bash的组合测试条件
方式1:
COMMAND1 && COMMAND2
COMMAND1 || COMMAND2
! COMMAND
方式2:
EXPRESSION1 -a EXPRESSION2     #在test中-a有两种用法:1.用于判断文件是否存在;2.用于判断,效果等同于&&
EXPRESSION1 -o EXPRESSION2      #同上
! EXPRESSION
条件测试test,[];条件性的执行操作符;bash的组合测试条件
复制代码

 

 [[ $n =~ ^[:digit:]+$ ]]    #判断是否为数字

 流程控制

复制代码
if条件判断
======================================================
if 条件1;then 代码1; elif 条件2;then 代码2;else 代码3;fi   #if语句可以写在一行,但是建议分行,结构更清晰
-------------------------
if [ 条件判断式1 ]              #if使用分行书写时,';'可以不写!
    then
        当条件判断式1成立时,执行程序1
elif [ 条件判断式2 ]
    then
        当条件判断式2成立时,执行程序2
else
    当所有条件都不成立时,最后执行此程序
fi
-------------------------
if [ 条件判断式1 ] ;then
    当条件判断式1成立时,执行程序1
elif [ 条件判断式2 ];then
    当条件判断式2成立时,执行程序2
else
    当所有条件都不成立时,最后执行此程序
fi

=======================================================
if后面可以跟随test的判断,也可以跟随一个命令;猜测本质其实就是判断test或者命令的结果,即判断$?的值
if [ $n1 -gt 100 ];
if ping -c1 192.168.1.1if ping -c1 192.168.1.1 &> /dev/null ; then echo "up";else echo "down"; fi
read -p 'please input command:' cmd;if $cmd
read -p 'please input command:' cmd;if `echo $cmd`

--------------------------------------------------------------------
示例1:
    read -p "please input a number" n1
    if [ $n1 -gt 100 ];then 
        echo "$n1 > 100" 
    elif [ $n1 -gt 50 ];then 
        echo "$n1 > 50"
    else 
        echo "$n1 < 50"                   
    fi

示例2:
    read -p 'please input command:' cmd                       
    echo "your command is: $cmd"
    if $cmd>/dev/null;then
        echo right
    else
        echo wrong
        exit 1
    fi  
if条件判断
复制代码
复制代码
case条件语句
case语句和if…elif…else语句一样都是多分支条件语句,不过和if多分支条件语句不同的是,case语句只能判断一种条件关系,而if语句可以判断多种条件关系。
这个语句需要注意以下内容:
    case语句,会取出变量中的值,然后与语句体中的值逐一比较。如果数值符合,则执行对应的程序,如果数值不符,则依次比较下一个值。如果所有的值都不符合,则执行 “*)” (*代表所有其他值)中的程序。
    case语句以“case”开头,以“esac”结尾。
    每一个分支程序之后要通过“;;”双分号结尾,代表该程序段结束(千万不要忘记,每次写case语句,都不要忘记双分号)。
======================================================
case $变量名 in
    "值1")
    如果变量的值等于值1,则执行程序1
    ;;
    "值2")
    如果变量的值等于值2,则执行程序2
    ;;
    …省略其他分支…
    *)
    如果变量的值都不是以上的值,则执行此程序
    ;;
esac

-------------------------------------------------------
示例:
#!/bin/bash
read -p "请输入一个字符,并按Enter确认:" KEY
case "$KEY" in
    [a-z]|[A-Z])
    echo "您输入的是字母"
    ;;
    
    [0-9])
    echo "您输入的是数字"
    ;;
    
    *)
    echo "您输入的是其他字符"
    ;;
esac
case条件语句
复制代码

 

复制代码
for循环、for循环开启多进程、如何传入一个动态次数的for循环
======================================================
for循环
语法一:
    这种语法中for循环的次数,取决于in后面值的个数(空格分隔),有几个值就循环几次,并且每次循环都把值赋予变量。也就是说,假设in后面有三个值,for会循环三次,第一次循环会把值1赋予变量,第二次循环会把值2赋予变量,以此类推。
    for 变量 in 值1 值2 值3 …(可以是一个文件等)
        do
            程序
        done
    
语法二:
    初始值:在循环开始时,需要给某个变量赋予初始值,如i=1;
    循环控制条件:用于指定变量循环的次数,如i<=100,则只要i的值小于等于100,循环就会继续;
    变量变化:每次循环之后,变量该如何变化,如i=i+1。代表每次循环之后,变量i的值都加1。
    for (( 初始值;循环控制条件;变量变化 ))
        do
            程序
        done
        
=====================================================
语法一示例:
#直接给出列表
for num in 1 2 3 4 5 6; do echo "The number is $num";done
#整数列表
for num in {1..6}; do echo "The number is $num";done
#返回列表的命令
for num in $(seq 6); do echo "The number is $num";done
for num in $( ls ); do echo "The number is $num";done
for num in `ls`; do echo "The number is $num";done


sum=0;for num in {1..100};do sum=$(($sum+$num)); done ;echo $sum
sum=0;for num in {1..100};do let sum+=$num; done ;echo $sum

------------------------
for i in {1..100}; do 
        { if ping -c1 192.168.1.$i &> /dev/null ;then 
                echo 192.168.1.$i is up ; 
        else 
                echo 192.168.1.$i is down ; 
        fi; }&                  #这里开启多线程了????????
done 
wait            #等待所有子线程结束后再退出

------------------------
for i in {1..10};do
    cat >> f1 << EOF
    aaa
    bbb
EOF         #注意,此处EOF不能与上面内容“aaa/bbb”同列
done
------------------------
for i in {1..10};do
        cat >> f1 <<-EOF
        aaa
        bbb
        EOF     #若要在同一行,则在第一个EOF前面加上“-”
done


======================================================

如何传入一个动态次数的for循环
for i in {1..$n};#错误!
for i in `seq $n`
for i in `eval echo {1..$n}`; #这时才是要的写法;为什么需要这样?猜测:``内是不会识别变量的,这里通过eval来识别$n
for循环、for循环开启多进程、如何传入一个动态次数的for循环
复制代码
复制代码
while循环、while循环的特殊用法(逐行处理文件的每一行内容)
======================================================
对while循环来讲,只要条件判断式成立,循环就会一直继续,直到条件判断式不成立,循环才会停止。
while [ 条件判断式 ]
    do
        程序
    done


-------------------------------------------------
SLEEPTIME=10        #生产中实用的小脚本,检测httpd的状态,当httpd挂了,实现自动拉起的功能
while true;do       #脚本未实测,可能有问题
        if killall -0 httpd &> /dev/null;then
                :
        else
                systemctl restart httpd &> /dev/null
                echo "xxxxxxxx" >> /data/httpd.log
        fi
        sleep $SLEEPTIME=10
done
-----------------------------------------------

num1=$[$RANDOM%100]
while : ; do
        read -p 'please input a number:' num2
        if [ $num1 -gt $num2 ] ;then
                        echo "$num2 is smaller"
        elif [ $num1 -lt $num2 ] ;then
                        echo "$num2 is biger"
        else
                echo "right!"
                break
        fi
done
echo "game is over"


==================================================
while循环的特殊用法
while read line;do
    循环体
done < /path/filename         #实现功能:逐行读取/path/filename文件的每一行,并且将内容赋值给变量line
----------------------------
df | while read line;do     #此处的df就是普通的df命令,可以替换为其他命令,如ls等;其实这个就是另一种形式的特殊用法
    循环体
done
while循环、while循环的特殊用法(逐行处理文件的每一行内容)
复制代码

 until循环:和while循环相反,until循环时只要条件判断式不成立则进行循环,并执行循环程序。一旦循环条件成立,则终止循环。

 

复制代码
select循环与菜单
==================================================
    select循环主要用于创建菜单,将数字顺序排列的菜单项将显示在标准错误上,并显示PS3提示符,等待用户输入
    用户输入菜单列表中的某个数字,执行相应的命令
    用户输入被保存在内置变量REPLY中

select variable in list
    do
        循环体命令
    done
select是个无限循环,因此需要用break、exit来终止循环,也可以ctrl+c退出循环
select经常和case联合使用
================================================
select choice in choice1 choice2 choice3 choice4;do
    case $choice in
    choice1)
        echo you choose $choice 
        ;;
    choice2)
        echo you choose $choice 
        ;;
    choice3)
        echo you choose $choice 
        ;;
    choice4)
        echo you choose $choice 
        ;;
    *)
        echo you choose none!
        ;;
    esac
done
select循环与菜单
复制代码
复制代码
信号捕捉trap
==========================================
信号捕捉trap
trap命令的参数分为两部分,前一部分是接收到指定信号时将要采取的行动,后一部分是要处理的信号名。
https://blog.csdn.net/weixin_30703911/article/details/96862888
它有三种形式分别对应三种不同的信号回应方式。
    第一种:
    trap "commands" signal-list
    当脚本收到signal-list清单内列出的信号时,trap命令执行双引号中的命令。

    第二种:
    trap signal-list
    trap不指定任何命令,接受信号的默认操作,默认操作是结束进程的运行。

    第三种:
    trap " " signal-list
    trap命令指定一个空命令串,允许忽视信号,我们用到的就是这一种。



查看信号种类   kill -l ;trap -l
kill默认使用的是信号15,这个信号是可以被捕捉
但是信号9(强杀)是不能被捕捉的    kill -9 PID
====================================================
示例:
实现功能:持续运行while循环,当捕捉到信号“HUP INT QUIT TSTP”时,执行双引号里的命令,而忽视信号
trap "echo catch signle " 1 2 3 20 
trap "echo catch signle " HUP INT QUIT TSTP     
i=0
while true;do
        echo $i
        ((i++))
        sleep 1
done

    猜测:其实脚本在运行时,是有默认的trap的,只不过trap为空。所以可以在脚本的不同位置修改不同的trap,那么在运行脚本的某个阶段则会执行相应的trap
信号捕捉trap
复制代码

 

复制代码
特殊流程控制语句:exit、continuebreak;shift
==============================================================================================
exit [返回值]
系统是有exit命令的,用于退出当前用户的登录状态。可是在Shell脚本中,exit语句是用来退出当前脚本的。也就是说,在Shell脚本中,只要碰到了exit语句,后续的程序就不再执行,而直接退出脚本。
如果exit命令之后定义了返回值,那么这个脚本执行之后的返回值就是我们自己定义的返回值。可以通过查询$?这个变量,来查看返回值。如果exit之后没有定义返回值,脚本执行之后的返回值是执行exit 语句之前,最后执行的一条命令的返回值。

----------------------------------------------------------------------------------------------
当程序执行到break语句时,会结束整个当前循环。而continue 语句也是结束循环的语句,不过continue 语句单次当前循环,而下次循环会继续。
continue也是结束流程控制的语句。如果在循环中,continue语句只会结束单次当前循环。
continue    用于循环体中,提前结束本轮循环,进入下一轮循环的判断
    continue N      #N默认为1
break       用于打断循环
    break N         #N默认为1
----------------------------------------------------------------------------------------------
严格来说shift不属于流程控制吧....
shift       可以把脚本传入的参数看成一个列表,由左向右排序;shift就是用于移除左侧的参数,默认移除1个
    shift N         #N默认为1
    例如某个支持批量创建账号的脚本,参数即账号名,但是数据不固定。此时可以利用shift,在while循环中一直对第一个参数进行判断,有参数则进行账号创建,没参数则退出循环
特殊流程控制语句:exit、continue、break;shift
复制代码

 

 

整理思路参照:https://www.cnblogs.com/jiading/p/12302600.html
补充资料:https://blog.csdn.net/w918589859/article/details/108752592

posted @   雲淡風輕333  阅读(111)  评论(0编辑  收藏  举报
(评论功能已被禁用)
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示