第二章·shell编程-变量

变量介绍

什么是变量

量:是记录现实世界当中的某种状态

变:指的是记录的状态是可以发生变化的

name=lw
age=18

为什么使用变量

程序执行的本质就是一系列状态的变化,变是程序执行的直接体现,所以我们需要有一种机制能够反映或者说是保存下来程序执行时状态以及状态的变化。

是为了让计算机能够像人一样去将一个事物的状态记忆下来(存到计算机内)

存,永远不是目的。

目的是为了,取

让计算机以后在使用的时候,可以取出来。

例如:

1.在游戏中,英雄的等级为1,打怪升级(变)10级

2.游戏中人物名字:rowey,使用了改名卡,变为:lw

3.植物大战僵尸:僵尸的存活状态为True,被植物打死了,变为False

如何使用变量

变量名 赋值符号 变量值
name=lw     // shell中定义变量
name: lw    // yaml中定义变量

[root@m01 ~]# vim test.sh
#!/bin/bash
#Author: _rowey_
#Date: _1999-12-20_
#Name: _Print Message_

name=lw
age=18

echo $name
echo $age

[root@m01 ~]# sh test.sh
lw
18

定义变量的语法(分三部分):

1)变量名

相当于一个门牌号,便于取出变量值,是访问到值的唯一方式

2)赋值符号

将值的内存地址,绑定给变量名

3)变量值

用来表示状态

变量的使用规则:先定义,在通过变量名去引用。

file

定义变量名规范

变量名的命名规则:

1.大前提:变量名的命名应该能够反映出值记录的状态。

2.变量是用来访问变量值的,所以变量名应该遵循一定规范,来方便我们标识存到内存中值的功能。

1.变量名只能是 字母、数字或下划线的任意组合(区分大小写)
例如:
x=1
X=2
是两个变量

2.变量名的第一个字符不能是数字
例如:
1x=1
123_x=1

3.尽量不要使用命令设置变量
例如:[echo,ls,cd,pwd...]

4.不要用拼音
5.不可以使用中文

定义变量名方式

1.下划线(纯小写)

age_of_lw=18 #python推荐使用

2.驼峰体

AgeOfLw=73

3.不好的方式

1)变量名为中文、拼音
2)变量名过长
3)变量名词不达意

变量分类:

环境变量

环境变量介绍

系统创建的变量,在环境变量文件中

## 查看系统环境变量
env
declare
export

## 环境变量
LANG // 字符集语言
PATH // 该变量中的路径里的所有可执行文件,都可以直接执行,不需要加路径
PS1 // 命令提示符
UID // 当前登录用户的uid
HOSTNAME // 当前主机名
PWD // 当前工作路径
USER // 当前登录的用户名

## 历史记录相关
HISTSIZE        //history 命令输出的记录数.
HISTFILESIZE    //在 .bash_history 中保存命令的记录总数.
HISTFILE        //历史命令保存文件的路径
TMOUT           //系统空闲等待时间
HISTCONTROL: export HISTCONTROL=ignorespace # 离职专用变量 //输入空格+命令 不显示在历史命令记录中
PROMPT_COMMAND # 跳板机专用变量
PS1-PS4介绍了一些用于提示信息控制的环境变量,而在此之前可以进行回调的一个环境变量就是PROMPT_COMMAND,这个环境变量中设定的内容将在交互式脚本的提示(PS1)出现之前被执行。

普通变量

普通变量介绍

局部变量,在脚本中自定义的变量

# 普通变量没什么好强调的,但是有一点,变量分隔一定要注意
## 还记得在做rsync同步的时候,以主机名,IP,时间命名目录
DATE=$(date +%F-%T)
IP=$(ifconfig eth0|awk 'NR==2{print $2}')

mkdir $DATE_$IP_$HOSTNAME ## 基本凉了

### 注意使用大括号分隔变量
mkdir ${DATE}_${IP}_${HOSTNAME}

# 与环境变量相关的文件
/etc/profile
/etc/bashrc
~/.bashrc
~/.bashrc_profile
/etc/profile.d/*.sh

特殊变量-位置

特殊变量介绍

匹配脚本参数,服务状态,特殊替换(重点)

应用场景:

1.提高书写脚本及脚本执行效率

2.判断服务状态、脚本参数、删除、替换

特殊变量-位置

符号 含义 应用
$0 脚本名字 脚本使用方法常用:给出错误提示或者使用帮助
$n 脚本的第N个参数 命令传参,传递给脚本,在脚本中使用
$# 统计脚本参数的个数 判断脚本是否传参
$* 获取脚本所有的参数 将所有参数当成一个整体,对传递的参数进行判断
$@ 获取脚本所有的参数 将所有参数当成一个整体,对传递的参数进行判断

$0:使用案例

用法

[root@m01 script]# vim 01_location_var.sh
#!/bin/bash
echo $0
[root@m01 script]# sh 01_location_var.sh
01_location_var.sh
[root@m01 script]# sh /script/01_location_var.sh
/script/01_location_var.sh

应用场景

# 脚本的错误提示
[root@m01 script]# /etc/init.d/network
Usage: /etc/init.d/network {start|stop|status|restart|reload|force-reload}

# 自己实现
[root@m01 script]# vim 01_location_var.sh
#!/bin/bash
echo "Usage: $0 {-t|-f|-e|-r|-a|-s|-d}"

[root@m01 script]# sh 01_location_var.sh
Usage: 01_location_var.sh {-t|-f|-e|-r|-a|-s|-d} 

$n:使用案例

[root@m01 script]# cat 02_location_var.sh
#!/bin/bash
echo $1 $2 $3 $4 $5 $6 $7 $8 $9 $10 $11

[root@m01 script]# sh 02_location_var.sh 1 2 3 4 5 6 7 8 9 10 11
1 2 3 4 5 6 7 8 9 10 11

##如果不用括号分开则会
[root@m01 script]# cat 02_location_var.sh
#!/bin/bash
echo $1 $2 $3 $4 $5 $6 $7 $8 $9 $10 $11
[root@m01 script]# sh 02_location_var.sh a b c d e f g h i j
a b c d e f g h a0 a1

#所以正确写法
[root@m01 script]# cat 02_location_var.sh
#!/bin/bash
echo $1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11}
[root@m01 script]# sh 02_location_var.sh a b c d e f g h i j
a b c d e f g h i j

$#:使用案例

作用可以让脚本,执行更加严谨

[root@m01 script]# cat 03_location_var.sh
#!/bin/bash
echo $1 $2
echo $#

[root@m01 script]# sh 03_location_var.sh

0

[root@m01 script]# sh 03_location_var.sh lw
lw
1
[root@m01 script]# sh 03_location_var.sh lw rowey
lw rowey
2
[root@m01 script]# sh 03_location_var.sh lw lw1 lw2
lw lw1
3

模拟脚本使用帮助

[root@m01 script]# vim 03_location_var.sh
#!/bin/bash
if [ $# -eq 0 ];then
  echo "Usage: $0 {start|stop|restart}"
else
  echo $1
fi

[root@m01 script]# sh 03_location_var.sh
Usage: 03_location_var.sh {start|stop|restart}
[root@m01 script]# sh 03_location_var.sh start
start

*$和$@:使用案例**

正常情况下,这两个变量是没有区别的

如果非要较真,那么,区别就在于,这两个变量是否加了引号

举个栗子:这里面需要用到for循环。

[root@m01 script]# vim 04_location_var.sh
## 没有加双引号
#!/bin/bash
echo '$*结果:'
for i in $*;do
  echo $i
done

echo '$@结果:'
for i in $@;do
  echo $i
done

[root@m01 script]# sh 04_location_var.sh lw1 lw2 lw3
$*结果:
lw1
lw2
lw3
$@结果:
lw1
lw2
lw3

## 加上双引号
[root@m01 script]# vim 04_location_var.sh
#!/bin/bash
echo '$*结果:'
for i in "$*";do
  echo $i
done

echo '$@结果:'
for i in "$@";do
  echo $i
done

[root@m01 script]# sh 04_location_var.sh lw1 lw2 lw3
$*结果:
lw1 lw2 lw3
$@结果:
lw1
lw2
lw3

特殊变量-状态

状态变量

符号 含义 应用
$? 上一条命令的返回值 判断命令是否执行成功
$$ 当前脚本运行的pid 在脚本运行时将pid记录到文件中,方便kill
$! 上一个运行脚本的pid
$_ 上一个命令或脚本的最后一个参数 类似于Esc+.

$?案列

检查域名是否通

#ping -c(设置 ping 的次数,默认无限次,可选) -i(设置 ping 的时间间隔,默认1秒,可选) -W(设置 ping 的超时时间,可选) 
[root@m01 script]# vim 01_status_var.sh
#!/bin/bash
ping  -c 1 -W 0.5 -i 0.1 www.baidu.com &>/dev/null
echo $?

[root@m01 script]# sh 01_status_var.sh
0

注意:并不是所有的命令执行成功都是0,所以我们在做判断之前,一定要在命令行执行一遍,确认一下。

特殊命令:

  • false
  • diff

例如:

[root@m01 script]# echo 1 > a.txt
[root@m01 script]# echo 2 > b.txt
[root@m01 script]# diff a.txt b.txt
1c1
< 1
---
> 2
[root@m01 script]# echo $?
1

[root@m01 script]# /bin/false
[root@m01 script]# echo $?
1

$$案例

[root@m01 script]# vim 02_status_var.sh
#!/bin/bash
echo $$
sleep 999

[root@m01 script]# sh 02_status_var.sh
86141

[root@m01 ~]# ps -ef|grep 86141
root      86141  80643  0 10:12 pts/0    00:00:00 sh 02_status_var.sh

变量子串

语法 含义
${parameter} 调用变量
${#parameter} 变量的长度
${parameter:offset} 截取变量,从offset之后
${parameter:offset:length} 截取变量,从offset之后取指定长度
${parameter#word} 从变量开头,删除最短匹配word的字串
${parameter##word} 从变量开头,删除最长匹配word的字串
${parameter%word} 从变量结尾,删除最短匹配word的字串
${parameter%%word} 从变量结尾,删除最长匹配word的字串
${parameter/pattern/string} 使用string替换第一个pattern
${parameter//pattern/string} 使用string替换所有的pattern

基础用法

# 1.直接调用变量
[root@m01 script]# vim 01_sub_string.sh

#!/bin/bash
name=lw
echo $name
echo ${name}
echo ${#name}

[root@m01 script]# sh 01_sub_string.sh
lw
lw
3

## 使用wc替换#方式(就很麻烦了)
[root@m01 script]# vim 01_sub_string.sh
#!/bin/bash
name=lw
echo $name
echo ${name}
echo ${#name}

num=`echo ${name}|wc -c`
echo "$num-1"|bc
echo ${name} |wc -L

[root@m01 script]# sh 01_sub_string.sh
lw
lw
3
3
3

## 使用awk替换#方式(了解)
[root@m01 script]# vim 01_sub_string.sh
#!/bin/bash
name=lw
echo $name
echo ${name}
echo ${#name}

num=`echo ${name}|wc -c`
echo "$num-1"|bc

echo ${name} |wc -L

echo ${name}|awk '{print length()}'

[root@m01 script]# sh 01_sub_string.sh
lw
lw
3
3
3
3

#########  对比效率
[root@m01 script]# time for n in {1..10000};do echo ${#n} >/dev/null;done

real    0m0.134s
user    0m0.082s
sys 0m0.051s

[root@m01 script]# time for n in {1..10000};do echo ${n}|wc -L >/dev/null;done

real    0m23.933s
user    0m10.648s
sys 0m12.865s

[root@m01 script]# time for n in {1..10000};do echo ${n}|awk '{print length()}' >/dev/null;done

real    0m29.773s
user    0m11.932s
sys 0m17.056s

截取用法

#!/bin/bash
# File Name: __a.sh__
# Version: __v1.1__ 
# Author: __rowey__ 
# Mail: __16275564442@qq.com__ 
# Blog: __https://roweyy.com__ 
# DateTime: __2022-07-19 10:18__
name=Luo_Wei

## 冒号后面你跟偏移量(offset)从0开始
[root@m01 script]# . a.sh 
i
[root@m01 script]# echo ${name:1}
uo_Wei

## 截取指定下标和长度
[root@m01 script]# echo ${name:3:4}
_Wei

替换和删除

#!/bin/bash
# File Name: __a.sh__
# Version: __v1.1__ 
# Author: __rowey__ 
# Mail: __16275564442@qq.com__ 
# Blog: __https://roweyy.com__ 
# DateTime: __2022-07-19 10:18__
name=Luo_Lao_Shi

## 不好使
[root@m01 script]# echo ${name#_}
Luo_Wei

[root@m01 script]# echo ${name#L}
uo_Wei

[root@m01 script]# echo ${name#*_}
Wei

## 必须配合※号使用除非写开头的字符
[root@m01 script]# name=Driver_Zeng_Lao_Shi
[root@m01 script]# echo ${name#*_}
Luo_Lao_Shi
[root@m01 script]# echo ${name##*_}
Shi

#如果学会这个,用处其实非常的大,有人可能会说,我用awk同样也可以取出来,为啥要用这个?

举个例子,有如下路径:
/etc/2.txt
/etc/sysconfig/3.txt
/root/abc/1.txt
/usr/local/src/a.txt

# 请打印出,以上文件的路径(不要文件名):
如果上题只要文件名,还好说awk就能解决。

### 取文件名
[root@m01 script]# cat path.txt |awk -F / '{print $NF}'
2.txt
3.txt
1.txt
a.txt

#!/bin/bash
for n in `cat path.txt`;do
  echo ${n##*/}
done

[root@m01 script]# sh 04_sub_string.sh
2.txt
3.txt
1.txt
a.txt

### 取路径

[root@m01 script]# vim 04_sub_string.sh
#!/bin/bash
for n in `cat path.txt`;do
  echo ${n%/*}
done

[root@m01 script]# sh 04_sub_string.sh
/etc/
/etc/sysconfig
/root/abc
/usr/local/src

## 替换
[root@m01 ~]# name='I am oldboy linux teacher'

[root@m01 ~]# echo ${name/oldboy/oldgirl}
I am oldgirl linux teacher

[root@m01 ~]# echo ${name/ /_}
I_am oldboy linux teacher
[root@m01 ~]# echo ${name// /_}
I_am_oldboy_linux_teacher

[root@m01 ~]# echo ${name//[a-z]/666}
666 666666 666666666666666666 666666666666666 666666666666666666666
[root@m01 ~]# echo ${name/[a-z]/666}
666 am oldboy linux teacher

注意: 替换,不会修改内存地址,变量还是原来的内存地址,内容也还是原来的内容

扩展变量

给变量设置默认值

写法 含义
${parameter:-string} 如果parameter没被赋值或其值为空,就以string作为默认值
${parameter:=string} 如果parameter没被赋值或其值为空,就以string作为默认值,并将string赋值给parameter
${parameter:?string} 如果parameter没被赋值或其值为空,就以string作为错误输出,否则显示parameter内容
${parameter:+string} 如果parameter没被赋值或其值为空,就什么都不做,否则用string替换变量内容

用法

## :-
[root@m01 ~]#  echo $dir

[root@m01 ~]# echo ${dir:-/tmp}
/tmp

[root@m01 ~]# dir=/backup
[root@m01 ~]# echo ${dir:-/tmp}
/backup

## :=
[root@m01 ~]# echo $dir1

[root@m01 ~]# echo ${dir1:=/tmp}
/tmp
[root@m01 ~]# echo $dir1
/tmp

## :?
[root@m01 ~]# echo $dir2

[root@m01 ~]# echo ${dir2:-/tmp}
/tmp
[root@m01 ~]# echo $dir2

[root@m01 ~]# echo ${dir2:?/tmp}
-bash: dir2: /tmp
[root@m01 ~]# echo $?
1

## :+
[root@m01 ~]# echo $dir3

[root@m01 ~]# echo ${dir3:+/tmp}

[root@m01 ~]# dir3=/backup
[root@m01 ~]# echo ${dir3:+/tmp}
/tmp
[root@m01 ~]# echo $dir3
/backup 

变量的赋值方式

  • 直接赋值 例: name=lw
  • 间接赋值,我们将一个命令执行的结果赋值给一个变量,例:IP=ifconfig eth0|awk 'NR==2{print $2}'
  • 交互赋值,我们在需要跟脚本进行交互 例:read
  • 传参赋值,执行脚本的时候,将需要传递的变量值,写在脚本的后面

交互式赋值变量

read:
read 选项 变量名
read 选项 -p '内容' 变量名

-p:打印内容
-s:不显示输入的内容
-t:设置超时时间
-a:将后面所有的结果放入数组

shell数据类型

1)字符串

2)整型

3)数组

很草率,其实在shell中,我们很少讲究数据类型,不像其他开发语言,光数据类型就要讲好几天。比如Python中数据类型:字符串,整型,浮点型,列表,数组,元组,字典...

而且不同的数据类型,也有不同的用户,并且每种数据类型都不鸡肋,而在shell中,好不容易有个比较特别的数据类型,他叫数组,然鹅,还很鸡肋。

字符串类型

# 赋值
name='lw'

# 取值
$name

整型

# 赋值
age=18

# 取值
$age

数组类型

# 赋值
## 方法1:
array=(lls cls wls)

## 方法2:
array[0]=lls
array[1]=cls
array[2]=wls

# 取值
${array[0]}
${array[1]}
${array[2]}
${array[*]}
${array[@]}

山林不向四季起誓 荣枯随缘