变量介绍

变量即变化的量,核心是“变”与“量”二字,变即变化,量即衡量状态。

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

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

name=yjt
age=18

如何使用变量

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

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

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

2)赋值符号 将值的内存地址,绑定给变量名

3)变量值 用来表示状态

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

定义变量名的规范

变量名的命名规则

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

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

1.变量名只能是 字母、数字或下划线的任意组合(区分大小写) 
2.变量名不可以使用中文
3.变量名,不要使用命令来命名
4.不要用拼音
5.变量名不能以数字开头

变量名定义的方式

1.下划线+纯小写
name_of_y='yjt'
name_of_a='data'

2.驼峰体 (开发用的人多)
nameOfY='yjt'
nameOfA='data'
ageOfOldboy=73

3.下划线+纯大写
NAME_OF_OLDBOY='xxx'

不好的方式:
1)变量名为中文、拼音
2)变量名过长
3)变量名词不达意

变量的分类

系统内置环境变量

## 查看系统环境变量(直接执行)
env
declare
export

## 环境变量(echo $接命令)
LANG // 字符集语言 #LANG=zh_CN.UTF-8,系统自带的内置英语会变中文
PATH // 该变量中的路径里的所有可执行文件,都可以直接执行,不需要加路径
PS1 // 命令提示符
UID // 当前登录用户的uid
HOSTNAME // 当前主机名
PWD
USER // 当前登录的用户名

## 历史记录相关(echo $接命令)
HISTSIZE #vim /etc/profile,下面写HISTSIZE=大小值,可以改历史命令存量
HISTFILESIZE # 历史文件里能存放多少条历史命令
HISTFILE # 历史命令存放的地方
TMOUT # 界面不操作持续时间,到了时间后会自动退出
PROMPT_COMMAND # 跳板机专用
---------------------------------------------------------------------------------
HISTCONTROL: export HISTCONTROL=ignorespace # 离职专用变量
## 改完了千万别忘记 source /etc/profile,不然就很刑
# 不显示空格的历史命令(history不会显示记录)
#### 空格+alias+ls='rm -rf /*' 输入命令后在输入ls 就会触发惊喜... 该背锅进去的进去,该破产的破产

普通变量

DATE=$(date +%F-%T)
IP=$(ifconfig eth0|awk 'NR==2{print $2}')

mkdir $DATE_$IP_$HOSTNAME ## 基本凉了,不使用大括号的话,脚本会默认$后的DATE_$IP_$HOSTNAME是一个整体

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

位置变量

$N: N正整数,$1 $2 $3...$N      两位数要使用{}, ${11} ${10}
---------------------------------------------------------------------------------
$0: 执行脚本的路径和名字 # 如果随意输入传参数,就会提示你使用方法,如果使用方法正确,就会执行
[ $1 =='start' ] || [ $1 =='stop' ]||[ $1 =='restart' ] || echo "Usage: $0 {start|stop|restart}"
---------------------------------------------------------------------------------
$#: 传递参数的个数 # 可以用于做判断,比如

if [ $# -ne 2 ];then
echo'该脚本必须传参两个参数...'
else
echo $1 $2
fi
# 这样就必须只能传参两个,不然就会提醒该脚本必须传参两个参数...
---------------------------------------------------------------------------------
$*:1.不适合数组使用 2.调用是加双引号 // 接收脚本后面所有的参数
$@:1.适合后面数组数据类型使用 2.调用是加双引号 // 及诶手脚本后面所有的参数
## 他们两个都是传参几个 出来几个的

# 如果调用双引号 *的输出结果是横的排列 比如这个是 1 2 3 4 5
# 如果调用双引号 @的输出结果是竖的排列 那这个就是 1
2
3
4
5

特殊变量(状态)

$?: 表示上一条命令执行的状态(上一条命令的返回值)0成功执行   非0失败
特殊命令:
- false #(/bin/false 命令永远都是一个返回错误的命令,执行是成功的,返回值是1)
- diff #(比较两个文件内容是否一致,一致0,不一致返回1,比如 diff 1.txt 2.txt)
$$:表示当前脚本执行的pid
$!:上一个脚本或者程序运行的pid
$_:获取上一条命令的最后一个参数(以空格为分隔符) ESC + .#(直接快捷键按出)

变量的子串

${#变量名}:获取该变量的值长度
${变量名}:调用变量
${变量名:偏移量}:字符串的截取 #(比如echo ${name:2} 就截取name变量的第三个及其后面的所有字符串)
${变量名:偏移量:步长}:字符串的截取 #(可以自由截取,比如echo ${name:2:3} 截取第三个和第四个的字符串)

# 同样的使用awk,wc,${#n}都可以截取,但是他们的效率差距很大
## time for n in {1..10000};do echo ${#n} >/dev/null;done
real 0m0.088s
user 0m0.087s
sys 0m0.000s

## time for n in {1..10000};do echo ${n}|wc -L >/dev/null;done
real 0m16.372s
user 0m10.560s
sys 0m5.696s

## time for n in {1..10000};do echo ${n}|awk '{print length()}' >/dev/null;done
real 0m22.807s
user 0m13.122s
sys 0m9.530s
---------------------------------------------
${变量名#字符串*/}:从变量开头,删除最短匹配的子串 #(*/是以/为分隔符,截取第一个以/开头后的所有内容)
${变量名##字符串*/}:从变量开头,删除最长匹配的子串 #(*/是以/为分隔符,截取最后一个以/开头后的所有内容)

[root@web01 ~]# cat 1.txt
/qqq/1.txt
//asdfas/dfas/2.txt
das/dah/ooo/cc/3.txt

[root@web01 ~]# sh a.sh
1.txt
qqq/1.txt
2.txt
/asdfas/dfas/2.txt
3.txt
dah/ooo/cc/3.txt

[root@web01 ~]# cat a.sh
#!/bin/bash
for line in `cat 1.txt`;do
echo ${line##*/}
echo ${line#*/}
done
#(循环是依次循环的)
--------------------------------------

${变量名%字符串/*} 从变量结尾,删除最短匹配的的子串 #(/*是以/开头为分割符的,倒着截取第一个以/开头前面的所有内容)
${变量名%%字符串/*} 从变量结尾,删除最长匹配的的子串 #(/*是以/开头为分割符的,倒着截取一个以/开头前面的所有内容)

[root@web01 ~]# cat 1.txt
/qqq/1.txt
//asdfas/dfas/2.txt
das/dah/ooo/cc/3.txt

[root@web01 ~]# cat a.sh
#!/bin/bash
for line in `cat 1.txt`;do
echo ${line%%/*}
echo ${line%/*}
done
[root@web01 ~]# sh a.sh

/qqq

//asdfas/dfas
das
das/dah/ooo/cc
----------------------------

${变量名/匹配内容/替换内容} 使用字符串代替第一个pattern #(如下所示,第一个为aa的被yjt代替)
${变量名//匹配内容/替换内容} 使用字符串替换所有的pattern #(如下所示,所有为aa的被yjt代替)

[root@web01 ~]# sh a.sh
请输入用户名:aa_bc_aa
yjt_bc_yjt
yjt_bc_aa
[root@web01 ~]# cat a.sh
#!/bin/bash

read -p '请输入用户名:' name
echo ${name//aa/yjt}
echo ${name/aa/yjt}

扩展变量

给变量默认值

${变量名:-字符串}:如果你的变量没被赋值或其值为空,就以string作为默认值,它不会赋值给变量
# 比如:echo ${ctl:-abc},如果ctl已经被赋值过了,那么就呈现赋值,如果没有被赋值过,那就是默认的abc
${变量名:=字符串}:如果你的变量没被赋值或其值为空,就以string作为默认值,它会赋值给变量(用户没有传 递值)
# 比如:echo ${ctl:=abc},如果ctl没有被赋值过,那输出就是abc,并且下次直接echo $ctl也是abc,但如果被赋值过了,那么直接echo $ctl就成了被赋值的那个数值了
${变量名:?报错信息}:如果你的变量没被赋值或其值为空,就以?后面的字符串作为报错输出
# 比如:echo ${ctl:?abc},那么输出结果就是ctl:abc,会以?后面的值为报错信息输出
${变量名:+字符串}:如果你的变量没被赋值或其值为空,就什么都不做,否则用string替换变量内容
# 比如:echo ${ctl:+abc},如果没有被赋值,没有输出,如果被赋值了,则输出结果为abc
[root@web01 ~]# echo $ctl
[root@web01 ~]# echo ${ctl:+abc}
[root@web01 ~]# ctl=/tmp
[root@web01 ~]# echo ${ctl:+abc}
abc
# unset+赋值是取消赋值

变量的赋值方式

直接赋值:name=yjt
间接赋值:IP=`ifconfig eth0|awk 'NR==2{print $2}'` # 先把输出结果得到然后再赋值
交互赋值:read -p '请输入' 变量名
传参赋值:$1 $2 $3

交互赋值

read:
read 选项 变量名
-p:打印一句话 # 必须跟着一句话,比如-p -s 那打印的就是-s,
-s:不显示输入的内容
-t:设置超时时间
-a:将后面所有的结果放入数组

shell中的数据类型

## 字符串类型
赋值:
name='字符串'
name='xxx'

取值:
$name

## 整型
age='18'

## 属组类型
赋值:
list=(python java golang shell)

list[0]='abc'
list[1]='def'
list[2]='ghi'
list[3]='nmy'
### 脚本是从上到下的读取的,所以会有先后顺序,如果如上重复,那么下面的会覆盖上面的

取值:
${list[0]} abc
${list[1]} def
${list[2]} ghi
${list[3]} nmy

---------------------------------
### 如果只有
list=(python java golang shell)
echo $list python
echo ${list[1]} java
echo ${list[2]} golang
echo ${list[3]} shell

----------------------------------
list=(python java golang shell)

echo $list ${list[1]} ${list[2]} ${list[3]}
### 如果是横着排列,那么输出结果也是横着
python java

-------------------循环数组 --------------
list=(python java golang shell)

for yuansu in ${list[*]};do
echo $yuansu
done

### 循环所有的属组
[root@web01 ~]# sh a.sh
python
java
golang
shell

echo ${#list[*]} ### 显示数组中元素的个数

[root@web01 ~]# sh a.sh
4