Shell脚本编程(小白基础学习)

前言

静态和动态语言:

*   静态编译语言:使用变量前,先声明变量类型,之后类型不能改变,在编译时检查,如Java语言、C语言
*   动态编译语言:不事先声明,可随时改变类型,如Shell语言、python语言、javascript语言、php语言

强类型和弱类型语言:

*   强类型语言:不同类型数据操作,必须经过强制转换成同一类型才能运算,如java,C#
*   弱类型语言:语言的运行时会隐式地做数据类型转换。无需指定类型,默认均为字符型;参与计算会自动进行隐式类型转换;变量无需事先定义可直接调用。如Shell语言,php,JavaScript

脚本检查工具:

 一、Shell脚本语言的基本结构

 1.1 Shell脚本的用途:

*   自动化常用命令
*   执行系统管理和故障排除
*   创建简单的应用程序
*   处理文本或文件

 1.2 Shell脚本基本结构:

 Shell脚本编程:是基于过程式,解释执行的语言

编程语言的基本结构:

*   各种系统命令的组合
*   数据存储:变量,数组
*   表达式:a+b
*   控制语句:if、case、for、while

shell脚本:包含一些命令或声明,并符合一定格式的文本文件

格式要求:首行执行shebang机制
#声明后续语句是通过那种语言写的
#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl

1.3 创建Shell脚本过程

1. 使用vim创建文本文件,第一行必须包括shell声明序列:
   #!/bin/bash

2. 加执行权限,给予执行权限,在命令行上指定脚本的绝对或相对路径
   chmod   +x   shellScript/hello.sh 

3. 运行脚本,直接运行解释器,将脚本作为解释器程序的参数运行。
 /root/shellScript/hello.sh 

 1.4 脚本注释规范

*   第一行一般为调用使用的语言
*   程序名,避免更改文件名为无法找到正确的文件
*   版本号
*   更改后的时间
*   作者相关信息
*   该程序的作用,及注意事项
*   最后是各版本的更新简要说明

 1.5 第一个脚本

#!/bin/bash
echo "My hostname is $(hostname)"
echo "Time is $(date +'%F %T')"
 
chmod +x hostname.sh
 
./hostname.sh
 

1.6 执行脚本

1、增加执行权限,执行脚本时会创建一个子shell,不影响现有的shell环境

chmod  +x  2.sh
./2.sh
/root/shellScript/2.sh

2、使用 . 或者source,执行脚本时不会创建一个子shell,会影响现有的shell环境

source 2.sh
. 2.sh

注意:尽量不要使用该方式执行脚本!!!

课后实例:备份脚本

1.7 脚本错误

*   语法错误,会导致后续的命令不继续执行,可以用**bash -n shellname**检查错误
*   命令错误,后续的命令还会继续,可以使用**bash -x shellname**检查
*   逻辑错误,只能使用**bash -x**进行观察

 1.8 Shell字符串详解

字符串(String)就是一系列字符的组合。字符串是Shell编程中最常用的数据类型之一

字符串可以由单引号`''`包围,也可以由`""`包围,也可以不用引号,三种方式的区别

1.  由单引号`' '`包围的字符串
    *   任何字符都会原样输出,在其中使用变量是无效的
    *   字符串中不能出现单引号,即使对单引号进行转义也不行
2.  由双引号`" "`包围的字符串
    *   如果其中包含了某个变量,那么该变量就会被解析(得到该变量的值),而不是原样输出
    *   字符串中可以出现双引号,只要进行转义就行
3.  不被引号包围的字符串
    *   不被引号包围的字符串中出现变量也会被解析,这一点和双引号`""`包围的字符串一样
    *   字符串中不能出现空格,否则空格后面的字符串会作为其他变量或者命令解析

通过代码演示一下三种形式的区别

#!/bin/bash
n=74
str1=c.biancheng.net$n 
str2="shell \"Script\" $n"
str3='C语言中文网 $n'
echo $str1
echo $str2
echo $str3


# 运行结果
c.biancheng.net74
shell "Script" 74
C语言中文网 $n

str1 中包含了`$n`,它被解析为变量 n 的引用。`$n`后边有空格,紧随空格的是 str2;Shell 将 str2 解释为一个新的变量名,而不是作为字符串 str1 的一部分

str2 中包含了引号,但是被转义了(由反斜杠`\`开头的表示转义字符)。str2 中也包含了`$n`,它也被解析为变量 n 的引用

str3 中也包含了`$n`,但是仅仅是作为普通字符,并没有解析为变量 n 的引用

获取字符串长度

在脚本语言中,字符串的拼接(也称为字符串连接或者字符串合并)往往都非常简单,例如:

*   在`PHP`中使用`.`即可连接两个字符串
*   在`JavaScript`中使用`+`即可将两个字符串合并为一个

然而,在Shell中你不需要使用任何运算符,将两个字符串并排放在一起就能实现拼接

#!/bin/bash
name="shell"
url="http://c.biancheng.net/shell/"
str1=$name$url #中间不能有空格
str1=$name":"$url
str2="$name $url" #如果被双引号包围,那么中间可以有空格,也可以出现别的字符串
str3="$name:$url" 
str4="${name}Script:${url}Index.html" #在变量后加上字符串,需要给变量名加上大括号

1.10 Shell字符串截取

Shell截取字符串通常有两种方式,从指定位置开始截取和从指定字符(子字符串)开始截取

从指定位置开始截取

这种方式需要两个参数:除了指定起始位置,还需要截取长度,才能最终确定要截取的字符串

既然需要指定起始位置,那么就要涉及到计数方向的问题,到底是从字符串左边开始计数,还是从字符串右边开始计数?答案是:`Shell同时支持两种计数方式`

1.从字符串左边开始计数

如果想从字符串的左边开始计数,那么截取字符串的具体格式如下:

${string:start:length}

`其中,Sting是要截取的字符串,start是起始位置(从左边开始,从0开始计数),length是要截取的长度(省略的话表示直到字符串的末尾)

2.从右边开始计数

如果想从字符串的右边开始计数,那么截取字符串的具体格式如下:
${string:0-start:length}

`同第 1) 种格式相比,第 2) 种格式仅仅多了0-,这是固定的写法,专门用来表示从字符串右边开始计数。

这里需要强调两点:
从左边开始计数时,起始数字是 0(这符合程序员思维);
从右边开始计数时,起始数字是 1(这符合常人思维)
计数方向不同,起始数字也不同。
不管从哪边开始计数,截取方向都是从左到右。

从指定字符(子字符串)开始截取

这种截取方式无法指定字符串长度,只能从指定字符(子字符串)截取到字符串末尾。Shell可以截取指定字符(子字符串)右边的所有字符,也可以截取左边的所有字符

1)使用#号截取右边字符

使用`#`号可以截取指定字符(或子字符串)右边的所有字符,具体格式如下:
${string#*chars}

#其中,string 表示要截取的字符,chars 是指定的字符(或者子字符串),*是通配符的一种,表示任意长度的字符串。*chars连起来使用的意思是:忽略左边的所有字符,直到遇见 chars(chars 不会被截取

使用`##`可以直到最后一个指定字符(子字符串)再匹配结束
${string##*chars}

例如:

#!/bin/bash
url="http://c.biancheng.net/index.html"
echo ${url#*/}
# 结果为:/c.biancheng.net/index.html
echo ${url##*/}
# 结果为:index.html

str="-----aa+++aa@@@"
echo ${str#*aa}
# 结果为:+++aa@@@
echo ${str##*aa}
# 结果为:@@@

2)使用%截取左边字符

使用`%`号可以截取指定字符(或者子字符串)左边的所有字符

${string%chars*}
`请注意*的位置,因为要截取 chars 左边的字符,而忽略 chars 右边的字符,所以*应该位于 chars 的右侧。其他方面%和#的用法相同

#!/bin/bash
url="http://c.biancheng.net/index.html"
echo ${url%/*}  #结果为 http://c.biancheng.net
echo ${url%%/*}  #结果为 http:
str="---aa+++aa@@@"
echo ${str%aa*}  #结果为 ---aa+++
echo ${str%%aa*}  #结果为 ---

 二、Shell脚本语言的变量用法详解

 2.1 变量

 变量表示命名的内存空间,将数据放在内存空间中,通过变量名引用,获取数据

 2.2 变量类型

变量类型:

*   内置变量:如PS1,PATH,UID,HOSTNAME,HISTSIZE
*   用户自定义变量
*   预定义变量
*   位置变量

不同的变量存放的数据不同,决定了以下:

1.  数据存储方式
2.  参与的计算
3.  表示的数据范围

变量数据类型:

*   字符串
*   数值:整型,浮点型(小数)、bash不支持浮点数

2.3 Shell中变量命名法则

*   不能使用程序中的保留字,如:if,for
*   只能使用数字,字母及下划线,且不能以数字开头
*   见名思意,用英文名字,并体现真正含义
*   统一命名规则:驼峰命名法
*   变量名大写
*   局部变量小写
*   函数名小写

 2.4 变量定义和引用

变量的生效范围(变量作用域)

*   普通变量:生效范围为当前shell进程;对当前shell之外的其他shell进程,包括当前shell的子shell进程均无效
*   环境变量:生效范围为当前shell进程及其子进程
*   本地变量:生效范围为当前shell进程中某代码片段,通常指函数

变量赋值:

name="value"

value可以是以下多种类型
直接字符串:name='root'
变量引用:name="$USER"
命令应用:name=`command`  || name=$(command)
通配符:FILE=/etc/* /*表示etc目录下所有的文件名*/

注意:变量赋值是临时生效,当退出终端后,变量会自动删除,无法持久保存。

弱引用和强引用:

*   “$name”:弱引用,其中的变量引用会被替换成为变量值
*   ‘$name’:强引用,其中的变量引用不会被替换成变量值,而保持原字符串

实例:

[root@localhost ~]# name='Mike'
[root@localhost ~]# NAME="$USER"
[root@localhost ~]# hostname=`hostname`
[root@localhost ~]# echo "My name is $name"
My name is Mike
[root@localhost ~]# echo "My name is $NAME"
My name is root
[root@localhost ~]# echo "My hostname is $hostname"
My hostname is localhost.localdomain
[root@localhost ~]# 

[root@localhost ~]# NUM=`seq 10`
[root@localhost ~]# echo $NUM
1 2 3 4 5 6 7 8 9 10
[root@localhost ~]# echo "$NUM"
1
2
3
4
5
6
7
8
9
10
[root@localhost ~]#

查看已定义的所有变量:

[root@localhost ~]#set

删除变量

[root@localhost ~]#unset shellname1 shellname2
//实例:
[root@localhost ~]# echo $name 
Mike
[root@localhost ~]# unset name
[root@localhost ~]# echo $name 

[root@localhost ~]# 

实例:"{}"的使用

[root@localhost ~]# NAME=mage
[root@localhost ~]# AGE=20
[root@localhost ~]# echo $NAME
mage
[root@localhost ~]# echo $AGE
20
[root@localhost ~]# echo $NAME $AGE
mage 20
[root@localhost ~]# echo $NAME_$AGE
20
[root@localhost ~]# echo ${NAME}_$AGE
mage_20
[root@localhost ~]# 

显示系统信息

[root@bogon ~]# name='Mike'
[root@bogon ~]# vim shell/os.sh
[root@bogon ~]# chmod +x shell/os.sh
[root@bogon ~]# ./os.sh
-bash: ./os.sh: No such file or directory
[root@bogon ~]# ./shell/os.sh 
----------Host systeminfo----------
HOSTNAME:  bogon
IPADDR:    192.168.79.147 
cat: /etc/redhat-release: No such file or directory
OSVERSION:  
KERNEL:    6.6.0-72.0.0.76.oe2403sp1.x86_64
CPU:        13th Gen Intel(R) Core(TM)
型号名称: 13th Gen Intel(R) 
MEMORY:    2.6Gi 
DISK:      200G 
----------       END     ----------

利用变量实现动态命令

[root@localhost ~]# CMD=hostname
[root@localhost ~]# $CMD
localhost.localdomain
[root@localhost ~]# 

 2.5 环境变量

环境变量:

*   可以使子进程(包括孙子进程)继承父进程的变量,但是无法让父进程使用子进程的变量。
*   一旦子进程修改了从父进程继承的变量,将会传递新的值给孙子进程
*   一般只在配置文件中使用,在脚本中较少使用

> 课程引入:普通变量生效的范围与环境变量生效的范围

[root@bogon ~]# vim shell/father.sh
[root@bogon ~]# vim shell/son.sh
[root@bogon ~]# chmod -R +x shell/*
[root@bogon ~]# ./shell/father.sh
father.sh:NAME=father
fatther is PID=24280
son.sh:NAME=
son.sh:NAME=son
son.sh PID is 24281
son.sh father pid is 24280

变量声明和赋值:

export name=VALUE
declare -x name =VALUE

#或者先赋值再声明~

value可以是以下多种类型
直接字符串:name='root'
变量引用:name="$USER"
命令应用:name=`command`  || name=$(command)
通配符:FILE=/etc/* /*表示etc目录下所有的文件名*/

2.6 只读变量

只读变量:只能声明定义,但后续不能修改和删除

声明只读变量:

readonly name
declare -r name

查看只读变量:

readonly [-p]
declare -r

2.7 位置变量

位置变量:在Bash Shell中内置的变量,在脚本代码中调用命令行传递给脚本的参数

$1,$2,... 对应第一个,第二个等参数,shift[n]换位置,最多9个
#预定义变量
$0  命令本身,包括路径
$*  传递给脚本的所有参数,全部参数合成一个字符串
$@  传递给脚本的所有参数,每个参数为独立字符串
$#  传递给脚本的参数的个数
$?    上个命令的退出状态,或函数的返回值
$$    当前shell进程ID。对于Shell脚本,就是这些脚本所在的进程ID

注意:$@,$*只有被双引号括起来的时候才会有差异

清空所有位置变量

set --
//写在脚本内部

实例演示1:

[root@bogon ~]# vim shell/ARG.sh
[root@bogon ~]# cat shell/ARG.sh 
#!/bin/bash
echo "1st arg is $1"
echo "2st arg is $2"
echo "3st arg is $3"
echo "4st arg is $4"
echo "The number of are is $#"
echo "All args are $*"
echo "All args are $@"
echo "The scriptname is `basename $0`"
[root@bogon ~]# vim shell/ARG.sh
[root@bogon ~]# chmod +x shell/ARG.sh 
[root@bogon ~]# chell/ARG.sh {1..10}
-bash: chell/ARG.sh: No such file or directory
[root@bogon ~]# shell/ARG.sh {1..10}
1st arg is 1
2st arg is 2
3st arg is 3
4st arg is 4
The number of are is 10
All args are 1 2 3 4 5 6 7 8 9 10
All args are 1 2 3 4 5 6 7 8 9 10
The scriptname is $
[root@bogon ~]# 

实例演示2:编写一个移动文件脚本

[root@bogon ~]# vim shell/move.sh
[root@bogon ~]# chmod +x shell/move.sh 
[root@bogon ~]# touch {a,b,c}
[root@bogon ~]# ls
'!'   !!   4.sh   anaconda-ks.cfg   c              nginx-1.29.1.tar.gz   shell
 !   3.sh   a      b                 nginx-1.29.1   nginx-1.29.1.zip
[root@bogon ~]# shell/move.sh a b c
 MOVE a b c to /tmp/2025-08-31_20-54-47 
[root@bogon ~]# tree /tmp/
/tmp/
├── 2025-08-31_20-54-47
│   ├── a
│   ├── b
│   └── c
├── systemd-private-aea0947d441047e8846aa012b10109de-chronyd.service-1C3WqT
│   └── tmp
├── systemd-private-aea0947d441047e8846aa012b10109de-polkit.service-7mL9zG
│   └── tmp
└── systemd-private-aea0947d441047e8846aa012b10109de-systemd-logind.service-nfdBj3
    └── tmp

8 directories, 3 files
[root@bogon ~]# 

2.8 退出状态码变量

 进程执行后,将使用变量 ? 保存状态码的相关数字,不同的值反应成功与失败, ?保存状态码的相关数字,不同的值反应成功与失败, ?保存状态码的相关数字,不同的值反应成功与失败,的取值范围为[0,255]

$?的值为0 代表成功
$?的值不为0 代表失败

用户可以在脚本中使用以下命令自定义退出状态码

exit   [n]

实例:

[root@localhost ~]# ping -c 2 www.baidu.com > /dev/null 
[root@localhost ~]# echo $?
0
[root@localhost ~]# cmd
-bash: cmd: 未找到命令
[root@localhost ~]# echo $?
127
[root@localhost ~]# 

注意:

*   脚本中一旦遇到了exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字
*   如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码

实例2:$?获取函数的返回值

#!/bin/bash
#得到两个数相加的和
function add(){
	return `expr $1 + $2`
}
add 23 50 #调用函数
echo $? #获取函数返回值
# 运行结果:
[root@localhost re_study]# bash test.sh
73

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值