shell编程速记 (shell脚本解读网站)

bash特性

1. 快捷键

 ^c             # 终止前台运行的程序
 ^z             # 将前台运行的程序挂起到后台
 ^d             # 退出 等价exit
 ^l             # 清屏
 ^a | home      # 光标移到命令行的最前端
 ^e | end       # 光标移到命令行的后端
 ^u             # 删除光标前所有字符
 ^k             # 删除光标后所有字符
 ^r             # 搜索历史命令

2. 通配符

 *                      # 匹配0或多个任意字符
 ?                      # 匹配任意单个字符
 [list]                 # 匹配[list]中的任意单个字符,或者一组单个字符[a-z]
 [!list]                # 匹配除list中的任意单个字符
 {}                     # 生成序列-数字与字母
 echo test{1..10}
 echo {01..100}         # 前面加0可使位数对齐
 echo test{,7,9}
 echo {1..10..2}

3. 引号

  • 双引号 " " 会把引号的内容当成整体来看待,允许通过$符号引用其他变量值,不支持通配符
  • 单引号 ' ' 所见即所得,会把引号的内容当成整体来看待,禁止引用其他变量值,shell*特中**殊符号都被视为普通字符
  • 反引号` `反引号和 $()一样,引号或括号里的命令会优先执行,如果存在嵌套,反撇号不能用
  • 不加引号,与双引号类似,可以支持通配符
  • () 开启子shell执行命令,$BASH_SUBSHELL不等于0
 echo "$(hostname)"
 # server
 echo '$(hostname)'
 # $(hostname)
 echo "hello world"
 # hello world
 echo 'hello world'
 # hello world
 echo $(date +%F)
 # 2018-11-22
 echo `echo $(date +%F)`
 # 2018-11-22
 echo `date +%F`
 # 2018-11-22
 echo `echo `date +%F``  # 存在嵌套,反撇号失效
 # date +%F
 echo $(echo `date +%F`)
 # 2018-11-22
 (pwd;echo $BASH_SUBSHELL;whoami)  # 开启子shell执行命令 BASH_SUBSHELL不等于0

变量定义

1. 直接定义

变量名=变量值 等号两边无空格

$变量名${变量名}的不同点:${变量名}可以只截取变量的一部分,而$变量名不可以

 A=hello            # 定义变量A
 echo $A            # 调用变量A
 echo ${A}          # 还可以这样调用
 unset A            # 取消变量
 
 a=${1:-"false"}  # 如果$1存在并且不为空那么a=$1,否则a=false
 a=${1:="false"}  # 如果$1不为空那么a=$1,否则$1=false且a=false
 a=${1:+"false"}  # 如果$1为空,什么都不做,否则a=false
 # ? 当变量没有赋值,提示错误信息,没有赋值功能
 a=${1:?"false"}  # 如果$1为空,false当作stderr输出,否则输出变量值
 echo ${var?}     # 变量没有被赋值,提示错误信息
 var=""
 echo ${var:?}    # 变量没有被赋值或赋空值时,提示错误信息
 
 # 提取子串
 ${string:position}  # 在string中从位置$position开始提取子串
 echo ${A:2:4}       # 表示从A变量中第3个字符开始截取,截取4个字符
 # 如果$string 为"*"或"@",那么将提取从位置$position开始的位置参数
 
 # 截取子串
 ${varible##*string} # 从左向右截取最后一个string后的字符串
 ${varible#*string}  # 从左向右截取第一个string后的字符串
 ${varible%%string*} # 从右向左截取最后一个string后的字符串
 ${varible%string*}  # 从右向左截取第一个string后的字符串
 
 # 替换子串
 ${varible/old/new}  # 匹配old,替换为new(从左往右第一个)
 ${varible//old/new} # 贪婪替换(替代所有)
 ${varible/old}      # 匹配old,删除
  • 取出一个目录下的目录和文件: dirnamebasename
 A=/root/Desktop/shell/mem.txt 
 echo $A
 /root/Desktop/shell/mem.txt
 dirname $A   # 取出目录
 /root/Desktop/shell
 basename $A  # 取出文件
 mem.txt
  • 变量"内容"的删除和替换
 # 一个“%”代表从右往左删除
 # 两个“%%”代表从右往左去掉最多
 # 一个“#”代表从左往右去掉删除
 # 两个“##”代表从左往右去掉最多
 url=www.taobao.com
 echo ${#url}            # 获取变量的长度
 14
 echo ${url#*.}
 taobao.com
 echo ${url##*.}
 com
 echo ${url%.*}
 www.taobao
 echo ${url%%.*}
 www
 # 替换:/ 和 //
 echo ${url/ao/AO}   # 用AO代替ao(从左往右第一个)
 echo ${url//ao/AO}  # 贪婪替换(替代所有)

2. 交互式定义

让用户自己给变量赋值,比较灵活

语法: read [选项] 变量名

常见选项

选项

释义

-p

定义提示用户的信息

-n

定义字符数(限制变量值的长度)

-s

不显示(不显示用户输入的内容)

-t

定义超时时间,默认单位为秒(限制用户输入变量值的超时时间)

举例

 # 用法1:用户自己定义变量值
 read name
 read -p "Input your name:" name
 
 # 用法2:变量值来自文件
 cat 1.txt 
 10.1.1.1 255.255.255.0
 read ip mask < 1.txt
 echo $ip
 10.1.1.1
 echo $mask
 255.255.255.0

3. 有类型定义

目的: 给变量做一些限制,固定变量的类型,比如:整型、只读

用法: declare 选项 变量名=变量值

常用选项

选项

释义

举例

-i

将变量看成整数

declare -i A=123

-r

定义只读变量

declare -r B=hello 等于 readonly B=hello

-a

定义普通数组;查看普通数组

-A

定义关联数组;查看关联数组

-x

将变量通过环境导出

declare -x AAA=123456 等于 export AAA=123456

举例

 declare -i A=123
 echo $A
 123
 A=hello
 echo $A
 0
 declare -r B=hello
 echo $B
 hello
 B=world
 -bash: B: readonly variable
 unset B
 -bash: unset: B: cannot unset: readonly variable

变量分类

1. 本地变量

  • 当前用户自定义的变量在当前进程中有效,其他进程及当前进程的子进程无效
  • 使用 source 或者 . 方式执行脚本,是在当前shell环境中执行脚本,可以保存变量

2. 环境变量

  • 当前进程有效,并且能够被 子进程 调用
  • env 显示当前shell中的全局变量
  • set | declare 显示当前shell中的所有变量,包括全局变量和局部变量
  • export 变量名=变量值 显示或设置环境变量
  • unset 删除变量或函数
 # 临时将一个本地变量(临时变量)变成环境变量
 export A=hello
 # 永久生效
 # vim /etc/profile 或者 ~/.bashrc
 export A=hello
 # 说明:系统中有一个变量PATH,环境变量
 export PATH=$PATH:/usr/local/mysql/bin

3. 全局变量

  • 所有的用户和程序都能调用,且继承,新建的用户也默认能调用

相关配置文件

文件名

说明

备注

$HOME/.bashrc

当前用户的bash信息,用户登录时读取

定义别名、umask、函数等

$HOME/.bash_profile

当前用户的环境变量,用户登录时读取

$HOME/.bash_logout

当前用户退出当前shell时最后读取

定义用户退出时执行的程序等

/etc/bashrc

全局的bash信息,所有用户都生效

/etc/profile

全局环境变量信息

系统和所有用户都生效

$HOME/.bash_history

用户的历史命令

history -w 保存历史记录 history -c 清空历史记录

说明: 以上文件修改后,都需要重新source让其生效或者退出重新登录

  • 用户登录 系统 读取 相关文件的顺序
  • /etc/profile
  • $HOME/.bash_profile
  • $HOME/.bashrc
  • /etc/bashrc
  • $HOME/.bash_logout

4. 系统变量

  • 即内置bash中的变量,shell本身已经固定好了它的名字和作用

内置变量

含义

$?

上一条命令执行后返回的状态;状态值为0表示执行正常,非0表示执行异常或错误

$0

当前执行的程序或脚本名

$#

脚本后面接的参数的个数

$*

脚本后面所有参数,参数当成一个整体输出,每一个变量参数之间以空格隔开

$@

脚本后面所有参数,参数是独立的,也是全部输出

$1~$9

脚本后面的位置参数,$1表示第1个位置参数,依次类推

${10}~${n}

扩展位置参数,第10个位置变量必须用{}大括号括起来(2位数字以上扩起来)

$

当前所在进程的进程号

$!

后台运行的最后一个进程号 (当前终端)

!$

调用最后一条命令历史中的参数

$PWD

当前目录

$RANDOM

默认会产生0~32767的随机整数

$IFS

内部域分隔符

 #!/bin/bash
 OLD_IFS=$IFS  # 保存原始值
 IFS=#39;\t'     # 改变IFS的值
 ...
 ...
 IFS=$OLD_IFS  # 还原IFS的原始值

$*:表示将变量看成一个整体,$@:表示变量是独立的

 #!/bin/bash
 for i in "$@"
 do
 echo $i
 done
 
 echo "======我是分割线======="
 
 for i in "$*"
 do
 echo $i
 done
 
 # bash test.sh a b c
 a
 b
 c
 ======我是分割线=======
 a b c
 # 打印一个随机数
 echo $RANDOM
 # 查看系统上一次生成的随机数
 set|grep RANDOM
 # 产生0~1之间的随机数
 echo $[$RANDOM%2]
 # 产生0~3之间的随机数
 echo $[$RANDOM%4]
 # 产生0~9内的随机数
 echo $[$RANDOM%10]
 # 产生0~100内的随机数
 echo $[$RANDOM%101]
 # 产生50-100之内的随机数
 echo $[$RANDOM%51+50]
 # 产生三位数的随机数
 echo $[$RANDOM%900+100]

数*运学**算

默认情况下,shell只能支持简单的整数运算

1. 运算内容

加(+)、减(-)、乘(*)、除(/)、求余数(%)、++、+=

2. 运算符号

表达式

举例

$(( ))

echo $((1+1))

$[ ]

echo $[10-5]

expr

expr 10 / 5

let

n=1; let n+=1 等价于 let n=n+1

数组

  • 普通数组:只能使用整数作为数组索引(元素的下标)
  • 关联数组:可以使用字符串作为数组索引(元素的下标)

1. 普通数组

  • 一次赋予一个值
 数组名[索引下标]=值
 array[0]=v1
 array[1]=v2
 array[2]=v3
 array[3]=v4
  • 一次赋予多个值
 数组名=(值1 值2 值3 ...)
 array=(var1 var2 var3 var4)
 array1=(`cat /etc/passwd`)   # 将文件中每一行赋值给array1数组
 array2=(`ls /root`)
 array3=(harry amy jack "Miss Hou")
 array4=(1 2 3 4 "hello world" [10]=linux)

2. 数组读取

 ${数组名[元素下标]}
 echo ${array[0]}            # 获取数组里第一个元素
 echo ${array[*]}            # 获取数组里的所有元素 *可替换为@
 echo ${#array[*]}           # 获取数组里所有元素个数 *可替换为@
 echo ${!array[*]}           # 获取数组元素的索引列表 *可替换为@
 echo ${array[@]:1:2}        # 访问指定的元素;1代表从下标为1的元素开始获取;2代表获取后面几个元素
 # 查看普通数组信息
 declare -a
 # for循环遍历数据
 for i in ${array[@]}
 do 
     echo $i
 done

3. 关联数组

声明关联数组

 declare -A asso_array1
 declare -A asso_array2
 declare -A asso_array3

关联数组赋值

  • 一次赋一个值
 # 数组名[索引or下标]=变量值
 asso_array1[linux]=one
 asso_array1[java]=two
 asso_array1[php]=three
  • 一次赋多个值
 asso_array2=([name1]=harry [name2]=jack [name3]=amy [name4]="Miss Hou")
  • 查看关联数组
 declare -A asso_array1='([php]="three" [java]="two" [linux]="one")'
 declare -A asso_array2='([name3]="amy" [name2]="jack" [name1]="harry" [name4]="Miss Hou")'
 declare -A | egrep "asso_array[12]"
  • 获取关联数组值
 echo ${asso_array1[linux]}  # 获取索引为liunx的值
 one
 echo ${asso_array1[*]}      # 获取数组值
 three two one
 echo ${!asso_array1[*]}     # 获取数组索引
 php java linux
 echo ${#asso_array1[*]}     # 获取数组元素个数
 3
  • 其他定义方式
 declare -A books
 declare -A | grep books
 let books[linux]++
 declare -A | grep books

条件判断

语法格式

  • test 条件表达式 更多判断使用man test查看
  • [ 条件表达式 ] 两边都要有空格
  • [[ 条件表达式 ]] 两边都要有空格,支持正则,两个变量的包含关系可使用=~匹配
  • ((条件表达式)) 两边不需要有空格

[ ][[ ]] 中对变量进行判断,需要加双引号

1. 判断文件类型

判断参数

含义

-e

判断文件是否存在(任何类型文件)

-f

判断文件是否存在并且是一个普通文件

-d

判断文件是否存在并且是一个目录

-L

判断文件是否存在并且是一个软连接文件

-b

判断文件是否存在并且是一个块设备文件

-S

判断文件是否存在并且是一个套接字文件

-c

判断文件是否存在并且是一个字符设备文件

-p

判断文件是否存在并且是一个命名管道文件

-s

判断文件是否存在并且是一个非空文件(有内容)

!

结合其他判断条件,表示取反

举例说明

 test -e file                # 只要文件存在条件为真
 [ -d /shell01/dir1 ]        # 判断目录是否存在,存在条件为真
 [ ! -d /shell01/dir1 ]      # 判断目录是否存在,不存在条件为真
 [[ -f /shell01/1.sh ]]      # 判断文件是否存在,并且是一个普通的文件

2. 判断文件权限

判断参数

含义

-r

当前用户对其是否可读

-w

当前用户对其是否可写

-x

当前用户对其是否可执行

-u

是否有suid,高级权限冒险位

-g

是否sgid,高级权限强制位

-k

是否有t位,高级权限粘滞位

3. 判断文件新旧

说明:这里的新旧指的是文件的修改时间

判断参数

含义

file1 -nt file2

比较file1是否比file2新

file1 -ot file2

比较file1是否比file2旧

file1 -ef file2

比较是否为同一个文件,或者用于判断硬连接,是否指向同一个inode

4. 判断数值

在 [ ] 和 test 中使用的比较符号

在 (( )) 和 [[ ]] 中使用的比较符号

含义

-eq

== 或 =

相等

-ne

!=

不等

-gt

>

大于

-lt

<

小于

-ge

>=

大于等于

-le

<=

小于等于

[ ]test[[ ]]中,支持-eq字符符号,以及支持< > = !=数学符号

[ ]中使用数学比较符号,需要添加转移符号\

 [ 1 \> 2 ] && echo yes || echo no

5. 判断字符串

判断参数

含义

-z

判断是否为空字符串,字符串长度为0则成立

-n

判断是否为非空字符串,字符串长度不为0则成立

string1 = string2

判断字符串是否相等

string1 != string2

判断字符串是否相不等

6. 逻辑运算符

在[ ] 和 test 中使用的操作符

在[[ ]] 和 (( )) 中使用的操作符

含义

举例

-a

&&

and,与

[ 1 -eq 1 -a 1 -ne 0 ] [ 1 -eq 1 ] && [ 1 -ne 0 ]

-o

||

or,或

[ 1 -eq 1 -o 1 -ne 1 ]

!

!

not,非

[ ! 1 -eq 1 ]

  • && 前面的表达式为真,才会执行后面的代码
  • || 前面的表达式为假,才会执行后面的代码
  • ; 只用于分割命令或表达式,不考虑前面的语句是否正确执行,都会执行;号后面的内容

举例说明

  • 数值比较
 [ $(id -u) -eq 0 ] && echo "the user is admin"
 [ $(id -u) -ne 0 ] && echo "the user is not admin"
 [ $(id -u) -eq 0 ] && echo "the user is admin" || echo "the user is not admin"
 
 uid=`id -u`
 test $uid -eq 0 && echo this is admin
 [ $(id -u) -ne 0 ]  || echo this is admin
 [ $(id -u) -eq 0 ]  && echo this is admin || echo this is not admin
 [ $(id -u) -eq 0 ]  && echo this is admin || echo this is not admin
 [[ 1 -eq 1 && 1 -ne 0 ]] && echo yes || echo no    # yes
 [[ ! 1 -eq 1 && 1 -ne 0 ]] && echo yes || echo no  # no
  • 类C风格的数值比较
 # 注意:在(( ))中,=表示赋值;==表示判断
 ((1==2));echo $?
 ((1<2));echo $?
 ((2>=1));echo $?
 ((2!=1));echo $?
 ((`id -u`==0));echo $?
 ((a=123));echo $a
 unset a
 ((a==123));echo $?
  • 字符串比较
 # 注意:双引号引起来,看作一个整体;= 和 == 在 [ 字符串 ] 比较中都表示判断
 a='hello world';b=world
 [ $a = $b ];echo $?
 [ "$a" = "$b" ];echo $?
 [ "$a" != "$b" ];echo $?
 [ "$a" == "$b" ];echo $?
 test "$a" != "$b";echo $?
 test -z $a;echo $?
 a=hello
 test -z $a;echo $?
 test -n $a;echo $?
 test -n "$a";echo $?
 [ '' = $a ];echo $?
 [[ '' = $a ]];echo $?
 [ 1 -eq 0 -a 1 -ne 0 ];echo $?
 [ 1 -eq 0 && 1 -ne 0 ];echo $?
 [[ 1 -eq 0 && 1 -ne 0 ]];echo $?

判断语句

1. if 结构

 if [ condition ];then
     command
     command
 fi
 
 if test 条件;then
     命令
 fi
 
 if [[ 条件 ]];then
     命令
 fi
 
 [ 条件 ] && command

2. if...else 结构

 if [ condition ];then
     command1
 else
     command2
 fi
 
 [ 条件 ] && command1 || command2

3. if...elif...else 结构

 if [ condition1 ];then
     command1
 elif [ condition2 ];then
     command2
 else
     command3
 fi

4. if 嵌套结构

 if [ condition1 ];then
     command1        
     if [ condition2 ];then
         command2
     fi
 else
     if [ condition3 ];then
         command3
     elif [ condition4 ];then
         command4
     else
         command5
     fi
 fi

5. case语句

case为多重匹配语句,如果匹配成功,执行相匹配的命令

语法结构

 # 说明:pattern表示需要匹配的模式
 case var in           # 定义变量;var代表是变量名
 pattern 1)            # 模式1;用 | 分割多个模式,相当于or
     command1          # 需要执行的语句
     ;;                # 两个分号代表命令结束
 pattern 2)
     command2
     ;;
 pattern 3)
     command3
     ;;
         *)            # default,不满足以上模式,默认执行*)下面的语句
     command4
     ;;
 esac                  # esac表示case语句结束

应用案例

 # 脚本提示让用户输入需要管理的服务名,然后提示用户需要对服务做什么操作,如启动,关闭等操作
 #!/bin/env bash
 read -p "请输入你要管理的服务名称(vsftpd):" service
 case $service in
         vsftpd|ftp)
         read -p "请选择你需要做的事情(restart|stop):" action
         case $action in
                 stop|S)
                 service vsftpd stop &>/dev/null && echo "该$serivce服务已经停止成功"
                 ;;
                 start)
                 service vsftpd start &>/dev/null && echo "该$serivce服务已经成功启动"
                 ;;
         esac
         ;;
         httpd|apache)
         echo "apache hello world"
         ;;
         *)
         echo "请输入你要管理的服务名称"
         ;;
 esac

循环语句

1. for循环

用于将一组命令执行 已知的次数

1)列表循环

  • 基本语法格式
 for variable in {list}
      do
           command 
           command
           …
      done
 # 或者
 for variable in a b c
      do
          command
          command
      done
  • 举例说明
 for var in {1..10};do echo $var;done
 for var in 1 2 3 4 5;do echo $var;done
 for var in `seq 10`;do echo $var;done
 for var in {0..10..2};do echo $var;done
 for var in {2..10..2};do echo $var;done
 for var in {10..1};do echo $var;done
 for var in {10..1..-2};do echo $var;done
 for var in `seq 10 -2 1`;do echo $var;done

2)类C风格循环

  • 基本语法结构
 for(( expr1; expr2; expr3 ))
     do
         command
         command
         …
     done
 # expr1:定义变量并赋初值
 # expr2:决定是否进行循环(条件)
 # expr3:决定循环变量如何改变,决定循环什么时候退出
 for (( i=1;i<=5;i++))
     do
         echo $i
     done
  • 举例说明
 for ((i=2;i<=10;i+=2));do echo $i;done
 # 计算1-100奇数和
 sum=0
 for i in {1..100..2}
 do
     sum=$[$i+$sum]
 done
 echo "1-100的奇数和为:$sum"
 # 或者
 sum=0
 for ((i=1;i<=100;i+=2))
 do
     let sum=$i+$sum
 done
 echo "1-100的奇数和为:$sum"

2. while循环

条件为真就进入循环,条件为假就退出循环

  • 基本语法格式
 while 表达式
 do
     command...
 done
 # 或者
 while  [ $i -eq 1 ] 或者 (( $i == 1 ))
 do
     command
     command
    ...
 done
  • 应用案例
 # 循环打印1-5数字
 i=1
 while (($i<=5))
 do
     echo $i
     let i++
 done
 
 # 计算1-50偶数和
 sum=0
 i=2
 while [ $i -le 50 ]
 do
     let sum=sum+i
     let i+=2
 done
 echo "1-50的偶数和为:$sum"

3. until循环

条件为假就进入循环,条件为真就退出循环

  • 基本语法格式
 until expression
     do
         command
         command
         ...
     done
 
 # 打印1-5数字
 i=1
 until [ $i -gt 5 ]
 do
     echo $i
     let i++
 done

4. 循环控制

循环体: do....done之间的内容

  • continue;继续,表示循环体内下面的代码不执行,重新开始下一次循环
  • break;打断,马上停止执行本次循环,执行循环体后面的代码
  • exit;退出,表示直接退出整个程序
  • shift;移位,使位置参数向左移动,默认移动1位,可以使用shift 2

5. 循环嵌套

一个循环体内又包含另一个 完整 的循环结构,称为循环的嵌套

每次外部循环都会触发内部循环,直至内部循环完成,才接着执行下一次的外部循环

for循环、while循环和until循环可以相互嵌套

  • 案例
 # 打印九九乘法表
 # Y轴:循环9次,打印9行空行
 # X轴:循环次数和Y轴相关;打印的是X和Y轴乘积 $[] $(())
 
 # for循环
 for ((y=1;y<=9;y++))
 do
     for ((x=1;x<=$y;x++))
     do
         echo -ne "$x*$y=$[$x*$y]\t"
     done
 echo
 echo
 done
 
 # while循环
 y=1
 while [ $y -le 9 ]
 do
     x=1
     while [ $x -le $y ]
     do
         echo -ne "$x*$y=$[$x*$y]\t"
         let x++
     done
 echo
 echo
 let y++
 done
 
 # until循环
 y=1
 until [ $y -gt 9 ]
 do
     x=1
     until [ $x -gt $y ]
     do
         echo -ne "$x*$y=$[ $x*$y ]\t"
         let x++
     done
 echo
 echo
 let y++
 done

函数

  • shell中允许将 一组命令集合 语句 形成一段 可用代码 ,这些代码块称为shell函数
  • 给这段代码起个名字称为函数名,后续可以直接调用该段代码的功能

1. 函数定义

 # 方法1
 函数名(){
   函数体(一堆命令的集合,来实现某个功能)
   return
 }
 
 # 方法2
 function 函数名(){
    函数体(一堆命令的集合,来实现某个功能)
    echo hello
    echo world
    return
 }

函数中可以定义位置参数,如 $1$2等,在函数调用时传入位置参数即可

函数中可以使用 local关键字,定义局部变量

函数中 return可以结束一个函数,return默认返回函数中最后一个命令状态值,也可以给定参数值,范围是0-256之间

2. 函数调用

  • 脚本中调用
 #!/bin/bash
 # 打印菜单
 menu(){
 cat <<-END
     h    显示命令帮助
     f    显示磁盘分区
     d    显示磁盘挂载
     m    查看内存使用
     u    查看系统负载
     q    退出程序
     END
 }
 menu    # 调用函数
  • 当前命令行调用
 cat func.sh  # 函数有入参,用$1 $2 ...表示
 #!/bin/bash
 hello(){
 echo "hello args is $1 and $2"
 hostname
 }
 
 source func.sh # or
 . func.sh 
 hello my world
  • 定义到用户环境变量
 # vim ~/.bashrc
 文件中增加如下内容:
 hello(){
 echo "hello $1"
 hostname
 }
 # 用户打开bash后可以直接使用该函数