shell脚本实战:

本文详细介绍了Shell脚本的基础知识,包括解释器与编译器的区别、脚本编写规范、执行方式、位置参数、预定义变量、条件判断、循环语句、函数定义与使用,以及数组操作。同时,展示了如何通过Shell脚本进行用户管理、字符串处理等实际应用,帮助读者深入理解Shell脚本的编写与执行流程。
摘要由CSDN通过智能技术生成

语言的类型

解释型

计算机不能直接理解任何除机器语言以外的语言,所以必须要把程序员所写的程序语言翻译成机器语言,计算机才能执行程序。

将其他语言翻译成机器语言的工具,被称为解释器。

编译器翻译的方式有两种:一个是编译,一个是解释。两种方式之间的区别在于翻译时间点的不同。当编译器以解释方式运行的时候,也称之为解释器。

  • 交给解释器来解释执行

  • 跨平台优势

  • 能够直接交付源码

  • 所见即所得

  • 可以通过解释器来执行源码

  • shell本身就是一个解释器

编译型

需要将代码编译成二进制文件

是不能直接看到源码的,查看的永远是一个编译好的文件

shell脚本编写

shell脚本的基本元素

  1. 第一行声明:#!/bin/bash

  2. 注释:# 、脚本的功能,作者,编写时间等

  3. 可执行语句、程序语句、条件测试、循环语句、判断、函数、数组、变量

shell文件格式

  1. 一般以.sh结尾,但.sh是给人看的,在系统中无意义

  2. 文件的第一行是解释器,一般使用/bin/bash,使用bash shell,来执行

  3. 注释:#单行注释

编写规范0

  1. 开头:指定脚本编译器: #!/bin/bash #!/bin/sh

  2. 加版本信息等 使用.vimrc文件能够快速生成开头注释信息

autocmd BufNewFile *.py,*.cc,*.sh,*.java exec ":call SetTitle()"
​
func SetTitle()
​
if expand("%:e") == 'sh'
​
call setline(1,"#!/bin/bash")
​
call setline(2,"##############################################################")
​
call setline(3, "# File Name: ".expand("%"))
​
call setline(4, "# Version: V1.0")
​
call setline(5, "# Author: sunyaping")
​
call setline(6, "# Email: sunyaping@yutianedu.com")
​
call setline(7, "# Created Time : ".strftime("%F %T"))
​
call setline(8, "# Description:")
​
call setline(9,"##############################################################")
​
call setline(10, "")
​
endif
​
endfunc
  1. 脚本尽量不用中文注释,也不必用cat,使用grep

  2. 代码缩进,段落要有注释 红帽的规范模板:/etc/.bashrc

  3. 最好以.sh为扩展名

shell脚本执行的方式

  1. 用相对路径执行脚本

  2. 用绝对路径执行脚本

  3. 直接使用bash来执行脚本

  4. 再当前shell中执行bash shell 脚本 source 或者 .

区别

如果使用bash指定运行一个脚本的话,不需要声明解释器,脚本可以没有执行权限

如果使用绝对和相对路径执行,需要有权限,

如果使用source 执行,是在当前shell中执行的,而其他的方法是开启了一个子shell

位置化参数

位置变量

这种变量主要是用来向脚本当中传递参数或数据的,变量名不能自定义,变量作用是固定的。

$1 $2 $3 $4 $5 $6 $7 $8 $9 ${10}

预定义变量

是Bash中已经定义好的变量,变量名不能自定义,变量作用也是固定的。

  • $0 :脚本名

  • $* :所有的参数,双引号引起来时参数视为单个字符串

  • $@: 所有的参数,双引号引起来时,每个参数作为个体

  • $# : 参数的个数

  • $$ : 当前进程的PID

  • $? : 上一个命令的返回值0表示成功

read

从命令行输入变量,接收命令行的输入,变成变量

read -p "输入的内容1:" A
read -p "输入的内容2:" B
echo $A $B 

read中也可以不指定变量,可以使用默认的变量REPLY

exit退出码

  • 程序执行成功之后会得到一个退出码

    • 0代表成功,1-255为失败

    • shell程序的退出码存放在$?中

    • exit 100 可以将退出码设定为100

条件运算符

用于链接命令

  • &&代表逻辑与

  • || 代表逻辑或

&&

  • com1 &&com2如果com1输出为0则执行com2反之不执行

||

  • com1 || com2 如果com1输出为0则不执行com2 反之执行

复合指令

() 在子shell中执行,不需要分号结尾 命令和括号不需要空格

{} 在当前shell执行,需要分号结尾 命令和括号需要空格

条件测试

文件测试表达式

操作符说明举例
-b file检测文件是否块设备文件,如果是,则返回true[ -b $file ] 返回false
-c file检测文件是否字符设备文件,如果是,则返回true[ -c $file ] 返回false
-d file检测文件是否目录,如果是,则返回true[ -d $file ] 返回false
-f file检测文件是否普通文件,如果是,则返回true[ -f $file ] 返回false
-g file检测文件是否设置了SGID位,如果是,则返回true[ -g $file ] 返回false
-k file检测文件是否设置了粘着位,如果是,则返回true[ -k $file ] 返回false
-p file检测文件是否块具名管道,如果是,则返回true[ -p $file ] 返回false
-u file检测文件是否设置了SUID,如果是,则返回true[ -u $file ] 返回false
-r file检测文件是否可读,如果是,则返回true[ -r $file ] 返回false

数值运算符

操作符说明举例
-eq检测两个数是否相等,相等返回true[ $a -eq $b ] 返回true
-ne检测两个数是否相等,不相等返回true[ $a -ne $b ] 返回true
-gt检测左边的数字是否大于右边的,如果是,返回true[ $a -gt $b ] 返回true
-lt检测左边的数字是否小于右边的,如果是,返回true[ $a -lt $b ] 返回true
-ge检测左边的数字是否大于等于右边的,如果是,返回true[ $a -ge $b ] 返回true
-le检测左边的数字是否小于等于右边的,如果是,返回true[ $a -le $b ] 返回true

布尔运算符

操作符说明举例
非运算,表达式为true,返回false,否则返回true[ ! true ] 返回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 ] 返回false

条件判断—if语句

单分支结构

第一种写法

if < 条件表达式 >
then 
    指令
fi 

第二种写法

if <条件表达式> ; then 
    指令
fi

双分支结构

if <条件表达式> ; then 
    指令1 
else 
    指令2 
fi

多分支结构

if <条件表达式1> ; then 
    指令1 
elif <条件表达式2> ; then 
    指令2 
else 
    指令3
else 
    指令n
fi

条件判断—case语句

case 变量名 in
值1)
    指令1
    ;;
值2)
    指令2
    ;;
值3)
    指令3
    ;;
*)
    指令4
    ;;
esac

循环语句—for循环

循环规律

赋予for一个变量,给变量赋值,对变量中的值进行循环语句

方法1:直接列出元素

for i in 12345 
do 
echo $i 
done 

方法2:使用大括号

for i in {1..5} 
do 
echo $i 
done 

方法3:使用seq

for i in $(seq 1 5) 
do 
echo $i 
done 

方法4:使用命令的结果

for i in $(ls *.sh)
do 
echo $i 
done 

循环语句—类C风格的for循环

#判定192.168.1.0网段主机存货状态
for ((i=1;i<=254;i++))
do 
    if ping -c 2 -W 1 192.168.1.$i > /dev/null ; then 
        echo "192.168.1.$i is up" 
    else 
        echo "192.168.1.$i is down" 
    fi 
done 

循环语句—while循环

循环规律

先进行测试条件,当测试条件为真,则执行循环体,反之则跳出循环,条件满足则一直循环,直到条件不满足

while <测试条件> 
do 
    循环体1 
    循环体2 
done

循环语句—until循环

循环规律

将循环体重复执行,知道条件成立,条件不满足则一直循环,直到条件满足

until <条件语句> 
do 
    循环体1 
    循环体2 
done 

循环语句—循环控制

break语句

作用是立刻跳出某个循环结构,直接退出、可以作用在for、while、until等循环体中

#!/bin/bash
for i in `seq 10` 
do 
    if [ $i -eq 4 ] ; then 
        break 
    fi 
    echo $i 
done 

continue语句

作用不是退出循环体,而是跳过当前循环体中该语句后面的语句,从循环语句的开始位置执行

#!/bin/bash 
for i in `seq 10` 
do 
    if [ $i -eq 4 ] ; then 
        continue
    fi 
    echo $i 
done 

脚本实例

批量创建用户:

1.创建用户,用户名来自于一个用户列表文件

2.给用户设置密码,密码随机

3.密码设置成功之后,显示该用户创建成功

for USER in $(cat /root/scripts/username.txt) 
​
do 
​
 useradd $USER &> /dev/null
​
 PASSWORD=$(openssl rand -base64 8)
​
 echo PASSWORD | passwd --stdin $USER &> /dev/null
​
 echo "username: $USER password: $PASSWORD" >> /root/scripts/userinfo
​
 if [ $? = 0 ];then
​
   echo create $USER succefully
​
 else
​
   echo create $USER failed
​
 fi
​
done

bash shell 循环打印一句中字母的长度不大于6的单词

word='i am a teacher from Hubei'
​
for i in $word
​
do 
​
  a=$(echo $i | wc -L)
​
   [ $a -lt 6 ] && echo $i 
​
done

在~/.vimrc中的内容:(用于自动生成脚本眉头)

autocmd BufNewFile *.py,*.cc,*.sh,*.java exec ":call SetTitle()"
​
func SetTitle()
​
if expand("%:e") == 'sh'
​
call setline(1,"#!/bin/bash")
​
call setline(2,"##############################################################")
​
call setline(3, "# File Name: ".expand("%"))
​
call setline(4, "# Version: V1.0")
​
call setline(5, "# Author: WangZhenDong")
​
call setline(6, "# Email: 2094169032@qq.com")
​
call setline(7, "# Created Time : ".strftime("%F %T"))
​
call setline(8, "# Description:")
​
call setline(9,"##############################################################")
​
call setline(10, "")
​
endif
​
endfunc

函数

什么是函数:

函数看成是功能模块,是由若干的shell语句组成的语句块,实现代码重复使用和模块化编程,作为shell程序的一部分来运行

函数的组成:

  • 函数名

  • 函数体

函数的格式:

格式1:

#简写格式
函数名 () {
    函数体(需要执行的内容) 
}

格式2:

#简写格式
function 函数名{
    函数体(执行内容)
}

格式3:

#标准格式
function 函数名() {
    函数体(执行内容)
}

定义函数的方式:

  • 在命令行定义

    • 只是临时有效

    [root@localhost ~]# function  redhat () { 
    > echo hello redhat 
    > }
    [root@localhost ~]# redhat
    hello redhat
  • 在脚本中定义

    • 在脚本中有效,一般在脚本的最前面引入

    #!/bin/bash 
    function redhat () {
    echo redhat 
    }
    redhat
    • 函数定义了不被使用的话,函数不会生效,必须要使用了函数,函数定义了才有作用

  • 通过文件的方式引入

    • 在不同的文件中写入函数,通过读文件的方式去读取函数,从而调用函数本身

    [root@localhost function]# cat hello1 
    function redhat() {
    echo hello redhat 
    }
    ​
    [root@localhost function]# cat hello2 
    function wuhan () {
    echo hello wuhan 
    }
    ​
    [root@localhost function]# cat hello.sh  
    . /root/function/hello1 
    . /root/function/hello2
    redhat 
    wuhan 
    ​
    [root@localhost function]# bash hello.sh  
    hello redhat
    hello wuhan

查看函数

declare -F              #查看当前已经定义的函数名
declare -f              #查看当前已经定义的函数定义
declare -f 函数名        #查看指定当前定义的函数名
declare -F 函数名        #查看指定的当前已经定义的函数名定义
​
-a 将变量定义为数组
-A 将变量定义为关联数组
-f 显示已经定义的所有函数名以及内容
-F 显示已经定义的所有函数名
-x 声明或者显示环境变量和函数,相当于export

删除函数

unset 函数名

环境函数

和环境变量一样,需要在子shell中持续使用后函数,则需要注册为环境函数

#建立环境函数
declare -xf 函数名
export -f 函数名
#查看环境函数
declare -f 
export -f 

函数的返回值

  • 函数的执行结果返回值

    • 使用echo等命令进行输出

    • 函数体中调用命令的输出结果

  • 函数的退出状态码

    • 默认取决于函数中执行的后一条命令退出的状态码

    • 自定义退出状态码格式为:

      • return 从函数中返回,用后状态命令决定返回值

      • return 0 无错误返回

      • return 1-255 有错误返回

      • echo $? 查看退出码

函数变量:

变量作用域

  • 本地变量

    • 只在当前shell进程有效,为执行脚本会启动专用子shell,因此本地变量的作用范围是当前shell脚本程序文件,包括脚本中的函数

      • 本地变量: 函数的生命周期,函数结束的时候,变量自动销毁

  • 环境变量

    • 当前shell和子shell中有效

      • 如果函数中定义了普通变量,并且名称和本地变量相同,则使用本地变量,由于普通变量和普通变量会冲突,建议函数中只使用本地变量

      • 函数中定义变量的方法: local 变量名=值

      • 变量的值是会覆盖的

数组

为什么要有数组?

有时需要同时定义多个变量,并且这些变量都用来做某一类事情这就将他们定义为一个数组,即为多个变量的集合。

数组的类型:

普通数组

  1. 使用数字作为索引,可以不用声明

[root@localhost ~]# redhat=(hello redhat) 
[root@localhost ~]# set | grep redhat                #set查看变量 
BASH_VERSINFO=([0]="4" [1]="4" [2]="19" [3]="1" [4]="release" [5]="x86_64-redhat-linux-gnu")
MACHTYPE=x86_64-redhat-linux-gnu
redhat=([0]="hello" [1]="redhat")

  1. 打印数组

[root@localhost ~]# windows=(win7 win8 win10 win11) 
[root@localhost ~]# echo ${windows[*]}
win7 win8 win10 win11
[root@localhost ~]# echo $windows 
win7

规律:

  • 声明数组需要使用()将数组内的内容括起来

  • 引用数组需要使用${}将数组括起来,并且引用的时候通过在数组后面加上[索引号]的方式来进行引用单个或多个数组值

  • 当数组作为变量调用的时候,通常只会使用数组的第一个值的结果

关联数组

  • 需要使用declare -A 进行声明

[root@localhost ~]# declare -A hubei 
[root@localhost ~]# hubei=([es]=td [xn]=gh) 
[root@localhost ~]# set | grep hubei 
hubei=([xn]="gh" [es]="td" )

数组赋值:

普通数组

  1. 一次赋值一个元素

[root@localhost ~]# wuhan[3]=sjc 
[root@localhost ~]# set | grep wuhan 
wuhan=([3]="sjc")

  1. 一次赋值全部元素

[root@localhost ~]# wuhan=(hhl yby mulan sjc) 
[root@localhost ~]# set | grep wuhan 
wuhan=([0]="hhl" [1]="yby" [2]="mulan" [3]="sjc")

  1. 赋值特定的元素

[root@localhost ~]# wuhan=([1]=ssj [2]=rgm)
[root@localhost ~]# set | grep wuhan 
wuhan=([1]="ssj" [2]="rgm")

关联数组

  1. 一次赋值一个元素

[root@localhost ~]# declare  -A hubei 
[root@localhost ~]# hubei[xn]=gh 
[root@localhost ~]# set | grep hubei 
hubei=([xn]="gh" )

  1. 一次赋值全部元素

[root@localhost ~]# declare  -A hubei 
[root@localhost ~]# hubei=([xn]=gh [yc]=lj [xy]=nrm)
[root@localhost ~]# set | grep hubei 
hubei=([xn]="gh" [xy]="nrm" [yc]="lj" )

引用数组

查看所有数组

  1. declare -a 查看普通数组

[root@localhost ~]# declare  -a 
declare -a BASH_ARGC=()
declare -a BASH_ARGV=()
declare -a BASH_COMPLETION_VERSINFO=([0]="2" [1]="7")
declare -a BASH_LINENO=()
declare -a BASH_SOURCE=()
declare -ar BASH_VERSINFO=([0]="4" [1]="4" [2]="19" [3]="1" [4]="release" [5]="x86_64-redhat-linux-gnu")
declare -a DIRSTACK=()
declare -a FUNCNAME
declare -a GROUPS=()
declare -a PIPESTATUS=([0]="0")
  1. declare -A 查看关联数组

[root@localhost ~]# declare -A 
declare -A BASH_ALIASES=()
declare -A BASH_CMDS=()

引用数组单个元素

${NAME[INDEX]}
​
如果省略[INDEX]则表示引用下标为0的元素

引用数组所有元素

${name[*]}
​
${name[@]}
​
数组的长度,即数组中元素的个数

删除数组

删除数组中的某一个元素

[root@localhost ~]# unset  wuhan[1] 
[root@localhost ~]# set | grep wuhan 
_=wuhan
wuhan=([0]="redhat1" [2]="redhat3")

删除整个数组

[root@localhost ~]# unset  wuhan 
[root@localhost ~]# set | grep wuhan 
_=wuhan

数组数据处理

数组切片

  1. 跳过两个元素取一个元素

[root@localhost ~]# wuhan=(a b c d e f g h) 
[root@localhost ~]# echo ${wuhan[@]:2:1} 
c
​
#规律
{name[@]:offset:number} 
offset:要跳过的元素个数
number:要取出的元素个数

  1. 数组追加元素

[root@localhost ~]# wuhan+=(uioui)
[root@localhost ~]# echo ${wuhan[*]}
a b c d e f g h uioui

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值