- 变量介绍
- 变量分类
- 环境变量
- 普通变量
- 特殊变量-位置
- 特殊变量-状态
- 变量子串(复杂,但实用)
- 扩展变量
- 变量的赋值
- 交互式赋值变量
- Shell 数据类型
-曾老湿, 江湖人称曾老大。
-多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。 -擅长Web集群架构与自动化运维,曾负责国内某大型金融公司运维工作。 -devops项目经理兼DBA。 -开发过一套自动化运维平台(功能如下): 1)整合了各个公有云API,自主创建云主机。 2)ELK自动化收集日志功能。 3)Saltstack自动化运维统一配置管理工具。 4)Git、Jenkins自动化代码上线及自动化测试平台。 5)堡垒机,连接Linux、Windows平台及日志审计。 6)SQL执行及审批流程。 7)慢查询日志分析web界面。
变量介绍
什么是变量 |
---|
变量即变化的量,核心是“变”与“量”二字,变即变化,量即衡量状态。
量:是记录现实世界当中的某种状态 变:指的是记录的状态是可以发生变化的
代码语言:javascript复制name=zls
age=18
为什么使用变量 |
---|
程序执行的本质就是一系列状态的变化,变是程序执行的直接体现,所以我们需要有一种机制能够反映或者说是保存下来程序执行时状态以及状态的变化。
是为了让计算机能够像人一样去将一个事物的状态记忆下来(存到计算机内)
存
,永远不是目的。
目的是为了,取
让计算机以后在使用的时候,可以取出来。
例如: 1.在游戏中,英雄的等级为1,打怪升级(变)10级 2.游戏中人物名字:Driver_Zeng,使用了改名卡,变为:曾老湿 3.植物大战僵尸:僵尸的存活状态为True,被植物打死了,变为False
如何使用变量 |
---|
[root@m01 ~]# vim test.sh
#!/bin/bash
#Author: _DriverZeng_
#Date: _1999-12-21_
#Name: _Print Message_
name=zls
age=18
echo $name
echo $age
[root@m01 ~]# sh test.sh
zls
18
定义变量的语法(分三部分): 1)变量名 相当于一个门牌号,便于取出变量值,是访问到值的唯一方式
2)赋值符号 将值的内存地址,绑定给变量名
3)变量值 用来表示状态
变量的使用规则:先定义,在通过变量名去引用。
定义变量名规范 |
---|
变量名的命名规则: 1.大前提:变量名的命名应该能够反映出值记录的状态。 2.变量是用来访问变量值的,所以变量名应该遵循一定规范,来方便我们标识存到内存中值的功能。
代码语言:javascript复制1.变量名只能是 字母、数字或下划线的任意组合(区分大小写)
例如:
x=1
X=2
是两个变量
2.变量名的第一个字符不能是数字
例如:
1x=1
123_x=1
3.尽量不要使用命令设置变量
例如:[echo,ls,cd,pwd...]
定义变量名方式 |
---|
变量名定义方式 1.下划线(纯小写)
代码语言:javascript复制age_of_oldboy=73 #python推荐使用
2.驼峰体
代码语言:javascript复制AgeOfOldboy=73
3.不好的方式
1)变量名为中文、拼音 2)变量名过长 3)变量名词不达意
变量分类
环境变量
环境变量介绍 |
---|
系统创建的变量,在环境变量文件中
代码语言:javascript复制# 查看方式
env
declare
export
# 重点记住
LANG
PATH
PS1
UID
HOSTNAME
# 了解历史相关变量
HISTSIZE
HISTFILESIZE
HISTFILE
TMOUT
HISTCONTROL: export HISTCONTROL=ignorespace # 离职专用变量
PROMPT_COMMAND # 跳板机专用变量
普通变量
普通变量介绍 |
---|
局部变量,在脚本中自定义的变量
代码语言:javascript复制# 普通变量没什么好强调的,但是有一点,变量分隔一定要注意
## 还记得在做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:使用案例 |
---|
用法
代码语言:javascript复制[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
应用场景
代码语言:javascript复制# 脚本的错误提示
[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:使用案例 |
---|
在此处n
代表的是整数, 该数值目前没有具体的范围限制,亲测,10000个参数也能接收,为什么会有这种帅比
的测试动作呢?因为曾经有一个家里专门做制杖
业务的学生问过我一个很睿智
的问题:老师老师,Shell脚本最多能接收多少个参数?
此时此刻我的心情就是:... 自行脑补,于是乎,我就写了一套脚本,专门来进行测试。倒也没测出个所以然来,因为越加参数,脚本执行的越慢,最后加到了10000个,我没有耐心了。网上去搜索呢,也没有人给出具体答案,那么我想范围估计是1 - 正无穷,一般来说,1-9就已经够我们用了。
代码语言:javascript复制[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]# sh 02_location_var.sh zls cls wls
zls cls wls zls0 zls1
根据需求写脚本
代码语言:javascript复制输入:02_location_var.sh start 显示:zls_service starting...
输入:02_location_var.sh stop 显示:zls_service stopping...
输入:02_location_var.sh restart 显示:zls_service restarting...
$#:使用案例 |
---|
作用可以让脚本,执行更加严谨
代码语言:javascript复制[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 zls
zls
1
[root@m01 script]# sh 03_location_var.sh zls wls
zls wls
2
[root@m01 script]# sh 03_location_var.sh zls wls cls
zls wls
3
模拟脚本使用帮助
代码语言:javascript复制[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
$*和$@:使用案例 |
---|
正常情况下,这两个变量是没有区别的
代码语言:javascript复制[root@m01 script]# sh 04_location_var.sh
xinghao:
at:
[root@m01 script]# sh 04_location_var.sh zls
xinghao: zls
at: zls
[root@m01 script]# sh 04_location_var.sh zls cls
xinghao: zls cls
at: zls cls
如果非要较真,也就是有些丧心病狂的面试官,非要问区别。那么,区别就在于,这两个变量是否加了引号
举个栗子:这里面需要用到for循环。
代码语言:javascript复制[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 zls cls wls
$*结果:
zls
cls
wls
$@结果:
zls
cls
wls
## 加上双引号
[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 zls wls cls
$*结果:
zls wls cls
$@结果:
zls
wls
cls
特殊变量-状态
状态变量 |
---|
符号 | 含义 | 应用 |
---|---|---|
$? | 上一条命令的返回值 | 判断命令的执行是否成功 |
$$ | 当前脚本运行的pid | 在脚本运行时将pid记录到文件中,方便kill |
$! | 上一个运行脚本的pid | |
$_ | 上一个命令或者脚本的最后一个参数 | 类似于ESC . |
$?案例 |
---|
检查域名是否通
代码语言:javascript复制[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,所以我们在做判断之前,一定要在命令行执行一遍,确认一下。
例如:
代码语言:javascript复制[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
变量子串(复杂,但实用)
这个东西,怎么说呢,比较复杂,不容易学懂,比较高级,很实用,但是不用行不行呢,有些时候可以用其他命令来代替,有些时候,就够呛了。当然,如果学不会这个,使用其他命令也可以,比如sed 、 awk 、grep这几个组合也能实现功能,但是效率肯定没有直接使用子串的效率高。
语法 | 含义 |
---|---|
${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=zls
echo $name
echo ${name}
echo ${#name}
[root@m01 script]# sh 01_sub_string.sh
zls
zls
3
## 使用wc替换#方式(就很麻烦了)
[root@m01 script]# vim 01_sub_string.sh
#!/bin/bash
name=zls
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
zls
zls
3
3
3
## 使用awk替换#方式(了解)
[root@m01 script]# vim 01_sub_string.sh
#!/bin/bash
name=zls
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
zls
zls
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
PS:企业面试题 I am oldboy linux's teacher driverzeng,welcome to our trainning.
请打印出这句话中,单词大于6的单词并显示个数。
# 思路分析
1.如何将一句话变成一个一个的单词
2.将每一个单词拿出来跟6进行对比
3.如果大于6就打印
4.如果大于6就打印出具体个数
# 思路转代码
echo "I am oldboy linux's teacher driverzeng,welcome to our trainning." |tr ',.' ' '
for word in I am oldboy linux's teacher driverzeng welcome to our trainning;do
if [ ${#word} -gt 6 ];then
fi
done
echo "单词:$word"
echo "长度:${#word}"
# 放入脚本
[root@m01 script]# vim 02_sub_string.sh
#!/bin/bash
text=`echo "I am oldboy linux's teacher driverzeng,welcome to our trainning." |tr ',.' ' '`
for word in $text;do
if [ ${#word} -gt 6 ];then
echo "单词:$word"
echo "长度:${#word}"
fi
done
[root@m01 script]# sh 02_sub_string.sh
单词:linux's
长度:7
单词:teacher
长度:7
单词:driverzeng
长度:10
单词:welcome
长度:7
单词:trainning
长度:9
## 美化
#!/bin/bash
text=`echo "I am oldboy linux's teacher driverzeng,welcome to our trainning." |tr ',.' ' '`
for word in $text;do
if [ ${#word} -gt 6 ];then
echo '------ 单词统计结果 ------'
echo "单词:$word"
echo "长度:${#word}"
fi
done
## 纯awk方法(简单了解,这里只是为了让你们看看awk的牛逼之处)
[root@m01 script]# echo "I am oldboy linux's teacher driverzeng,welcome to our trainning."|awk -F '[ ,.]' '{for(i=1;i<=NF;i ) if(length($i) >6) print "单词:"$i,"长度:" length($i)}'
# 美化
[root@m01 script]# echo "I am oldboy linux's teacher driverzeng,welcome to our trainning."|awk -F '[ ,.]' '{for(i=1;i<=NF;i ) if(length($i) >6) print "------ 单词统计结果------n" "单词:"$i"n长度:" length($i)}'
------ 单词统计结果------
单词:linux's
长度:7
------ 单词统计结果------
单词:teacher
长度:7
------ 单词统计结果------
单词:driverzeng
长度:10
------ 单词统计结果------
单词:welcome
长度:7
------ 单词统计结果------
单词:trainning
长度:9
截取用法 |
---|
## 冒号后面你跟偏移量(offset)从0开始
[root@m01 script]# echo ${name:6}
_Zeng
[root@m01 script]# echo ${name:1}
river_Zeng
## 截取指定下标和长度
[root@m01 script]# echo ${name:6:2}
_Z
替换和删除 |
---|
## 不好使
[root@m01 script]# echo ${name#_}
Driver_Zeng
[root@m01 script]# echo ${name#D}
river_Zeng
[root@m01 script]# echo ${name#*_}
Zeng
## 必须配合※号使用除非写开头的字符
[root@m01 script]# name=Driver_Zeng_Lao_Shi
[root@m01 script]# echo ${name#*_}
Zeng_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=zls
- 间接赋值,我们将一个命令执行的结果赋值给一个变量,例:IP=
ifconfig eth0|awk 'NR==2{print $2}'
- 交互赋值,我们需要跟脚本进行交互,例:read
- 传参赋值,执行脚本的时候,将需要传递的变量值,写在脚本的后面 1 2
交互式赋值变量
代码语言:javascript复制read -p 'string' var
-s:不显示输入内容
-t:设置超时时间
-a:将传递的变量使用数组接收
Shell 数据类型
1)字符串 2)整型 3)数组
很草率,其实在shell中,我们很少讲究数据类型,不像其他开发语言,光数据类型就要讲好几天。比如Python中数据类型:字符串,整型,浮点型,列表,数组,元组,字典...
而且不同的数据类型,也有不同的用户,并且每种数据类型都不鸡肋,而在shell中,好不容易有个比较特别的数据类型,他叫数组,然鹅,还很鸡肋。
字符串类型 |
---|
# 赋值
name='zls'
# 取值
$name
整型 |
---|
# 赋值
age=18
# 取值
$age
数组类型 |
---|
# 赋值
## 方法1:
array=(zls cls wls)
## 方法2:
array[0]=zls
array[1]=cls
array[2]=wls
# 取值
${array[0]}
${array[1]}
${array[2]}
${array[*]}
${array[@]}