shell 变量和引用
目录
一. 变量和引用
1.1 什么是变量
- 变量是在程序中保护用户数据的一段内存存储空间,变量名是内存空间的首地址
1.2 变量的名称
- 组成 :字母、数字、下划线组成,不能以数字开头
- 变量名称的长度,shell没有明确规定,但是为了增加可读性,建议使用较短的、见名知意的名称命名
- 规则
1.首字母必须为字母:a-z,A-Z(推荐)
2.中间不能由空格,可以使用下划线(_)
3.不能使用标点符号
4.不能使用bash中关键字,输入help查看bash的保留字
1.3 变量的类型
- 原则 :shell是一种动态类型语言和弱类型语言,变量是不分数据类型的,统一都使用字符串存储,但根据变量的上下文环境,允许程序执行一些不同的操作,如:比较、整数加减
- shell的变量数据类型
[root@node1 ~]# vim test1.sh
#!/bin/bash
#定义变量x,输入初始值123
x=123
#变量x加1
let "x+=1"
#输出变量x的值
echo "x=$x"
#替换x中1 的值为abc,并赋值给变量y
y=${x/1/abc}
#使用declare -i 声明变量y为整形变量(i 表示整型)
declare -i y
#输出y的值
echo "y=$y"
[root@node1 ~]# bash test1.sh
x=124
y=abc24
1.4 变量的定义
- 原则:直接使用,不需要变量声明
- 格式 :变量名=变量的值
- 例:
[root@node1 ~]# vim test2.sh
#定义变量a
a=1
#定义变量b
b="hello"
#定义变量c
c="hello word"
#定义路径
bak_dir=/data/backup
- =前后不能收空格
- 字符串类型建议使用双引号作为定界符引起,尤其是字符串中有空格
1.5 自定义变量
- 概念 :上述以赋值形态形成的变量定义形式称为自定义变量
- 引用变量值:
1.$变量名
2.${表达式或变量名}
例:
[root@node1 ~]# a=1024
[root@node1 ~]# echo $a
1024
[root@node1 ~]# echo ${a}
1024
- 查看变量
[root@node1 ~]# declare
[root@node1 ~]# set
- 取消变量
[root@node1 ~]# a=256
[root@node1 ~]# echo $a
256
[root@node1 ~]# unset a
[root@node1 ~]# echo $a
- 作用范围:只在当前shell起效
1.6 环境变量
- 环境变量又称为全局变量,可以在任意shell生效,环境变量又分为自定义变量和bash内置的环境变量,用户退出命令后该变量会丢失。若需要永久保存就必须写在文件中
- 定义环境变量
#法1
export 环境变量=值
#法2
变量名=值
export 变量名
#法3
declare -x 变量名=值
例子:
[root@node1 ~]# export back_dir1=/home/backup
[root@node1 ~]# NAME="zhang san"
[root@node1 ~]# export NAME
[root@node1 ~]# declare -x AGE=10
[root@node1 ~]# env #显示当前用户的环境变量
[root@node1 ~]# printenv #功能同上
[root@node1 ~]# export #同上
注意:以上定义的环境变量都是临时的,重启后会实现,若要永久生效,则写入到配置文件中
- 对比:
c语言 局部变量 全局变量
shell 自定义变量 环境变量
- shell环境变量存储的文件
- bash shell 初始化文件有:/etc/profile、~/.bash_profile、~/.bash_login、~./profile、~/.bashrc、/etc/bashrc
- 结论:
1.对于用户的环境变量设置,常见的是用户家目录下的.bashrc和。bash_profile
2.对于全局环境变量设置,常见的文件有:/etc/profile /etc/bashrc /etc/prodile.d 这三个配置文件,常用方法是直接在/etc/profile文件中写入全局变量,如果想要在登录后初始化或者显示加载的内容,只需要把脚本放在 /etc/profile.d 文件下即可
1.7 位置变量
- 概念 : 当一条命令或脚本执行时,后面可以跟多个参数,可以使用位置变量来表示该参数
sh test1.sh hello word 123 456
- 当执行test1.sh脚本时,第一个参数为hello到第四个参数可以使用特殊的符号表示,如 $1 $2 $3 .......
- 常见的位置变量
$0: 脚本名
$1-$9:1-9个参数
${10} :10以上的参数需要大花括号括起
$* :所有参数
$@:所有参数
$#:参数个数
$$: 当前进程的PID
$!:上一个后台进程的PID
$? :上一个命令
例:
[root@node1 ~]# vim test3.sh
#!/bin/bash
echo "第二个位置参数: $2"
echo "第1个位置参数: $1"
echo "第4个位置参数: $4"
echo "所有参数是 $@"
echo "所有参数是: $*"
echo "当前进程的PID值: $$"
echo "参数的个数是:$#"
[root@node1 ~]# bash test3.sh 1 2 3 4 5
第二个位置参数: 2
第1个位置参数: 1
第4个位置参数: 4
所有参数是 1 2 3 4 5
所有参数是: 1 2 3 4 5
当前进程的PID值: 2837
参数的个数是:5
[root@node1 ~]# vim test4.sh
#!/bin/bash
echo "name: $1"
echo "age: $2"
echo "E-mail: $3"
[root@node1 ~]# bash test4.sh 大漂亮 20 12345@qq
name: 大漂亮
age: 20
E-mail: 12345@qq
- $* 和$@区别
当$* 和$@没有被引用的时候,他们确实没有什么区别,都会把位置参数当成一个个体,$* 会把所有位置参数当成一个整体(或者说当成一个单词),如果没有位置参数,则$* 为空,如果有两个位置参数并且IFS为空格时,$* 相当于“$1 $2”
$@ 会把所有位置参数当成一个单独的字段,如果没有位置参数($#为0),则$@ 展开为空(不是空字符串,而是空列表),如果存在一个位置参数,则$@ 相当于$1 ,如果有两个参数,则“$@”相当于“$1 ” "$2"
[root@node1 ~]# set -- I am test command
[root@node1 ~]# for i in "$@"; do echo $i;done
I
am
test
command
[root@node1 ~]# for i in "$*"; do echo $i;done
I am test command
二、变量赋值和作用域
1.1 显示赋值 :变量名=变量值
1.2 read 从键盘读入变量值
- read 命令从标准输入中读取一行,并把输入行的每个字段的值指给shell变量
- 格式 :read -参数 变量名
参数 | 功能 |
-p | “提示语句:” 屏幕打印出一行提示语句 |
-n数字 | 当输入的字符数目达到预定数目时,自动退出,并将输入的数据赋值给变量,如:-n1,只要接受到一个字符就退出。只要按下一个字符进行回答,read命令立即接受输入并将其传给变量。无需按回车键 |
-t | 等待时间:计时输入,使用read命令存在潜伏危险。脚本很可能会停下来一直等待用户的输入。如果无论是否输入数据脚本都必须继续执行,那么可以使用-t选项指定一个计时器。-t 选项指定read命令等待输入的秒数。当计时满时,read命令返回一个非零退出状态 |
-s | 关闭回显,使read命令中输入的数据不显示在监视器上(实际上,数据是显示的,只是read命令将文本颜色设置成与背景相同的颜色) |
- 常用格式:
[root@node1 ~]# read -p "Enter Numbers:" num
Enter Numbers:7658
[root@node1 ~]# echo $num
7658
[root@node1 ~]# read -t 3 n1
[root@node1 ~]# read -s -p "Enter your passwd:" passwd
Enter your passwd:[root@node1 ~]# echo $passwd
123456
[root@node1 ~]# echo $REPLY
[root@node1 ~]# read
100
[root@node1 ~]# echo $REPLY #当输入时没有指定变量接收,会默认存储到REPLY变量中
100
#一次性输入多个变量的值
[root@node1 ~]# read t1 t2
12 45
[root@node1 ~]# echo $t1 $t2
12 45
- 面试题:总结4种赋值方式
1.直接赋值 :name="li si"
2.read命令 :read name
3.使用位置参数($1 $2 $3 $4...): name=$1
4.命令输入:name=$(whoami)
- 变量从文件中读取数据
[root@node1 ~]# cut -d ":" -f1 /etc/passwd > /user.list
[root@node1 ~]# for user in $(cat /user.list);do echo $user; done
[root@node1 ~]# while read user; do echo $user; done < /user.list
1.3 变量和引号
- 双引号 :出来$、单引号、反引号、反斜线之外,其他被引起的内容保存字面意思
- 单引号:所有字符保持字面意思
- 反引号:被引用的字符串转为shell命令
- 反斜线: 转义符(\),屏蔽后面字符的特殊含义
1.4 变量的作用域
- 全局变量:全局变量定义在脚本中,也可以定义在函数中,作用范围:从定义的开始处到shell脚本结束或者被显示的去除
[root@node1 ~]# vim test5.sh
#!/bin/bash
func() #定义函数
{
echo "$v1"
v1=200
}
v1=100
func
echo "$v1"
[root@node1 ~]# bash test5.sh
100
200
- 函数内部定义全局变量
#上例修改
[root@node1 ~]# vim test5.sh
#!/bin/bash
func() #定义函数
{
v2=200
}
func
echo "$v2"
[root@node1 ~]# bash test5.sh
200
- 局部变量:范围更小,仅限于某个程序段中,如:函数、shell等,通过local关键字定义:注意:函数的参数也是局部变量
[root@node1 ~]# vim test5.sh
#!/bin/bash
func() #定义函数
{
local v3=200 #使用local关键字声明为局部变量
}
func
echo "$v3"
[root@node1 ~]# bash test5.sh
1.5变量的运算
运算符 | 说明 |
---|---|
+、- | 求和、差 |
*、/、% | 求乘积,商,余数 |
** | 幂运算,例如3**3是求3的立方,即27 |
+=、-=、*=、/=、%= | 例a+=1相当于a=a+1 |
++variable、--variable | 先将变量variable的值加1,然后再赋给variable; 先将变量variable的值减1,然后再赋给variable |
variable++、variable-- | 先使用variable的值,然后再将该变量的值加1; 先使用variable的值,然后再将该变量的值减1 |
位运算符<<、>> | 位运算通常出现在整数间,它针对的不是整个整数,而是其二进制表示形式中的某个或者某些位(bit)。例如,2>>1是将二进制形式的2,即10,左移1位,从而变成100,即4。 左移,4<<2,将4左移2位,结果为16; 右移,8>>2,将8右移两位,结果为2 |
&、|、~、^ | 按位与,8&4,将8和4进行按位与运算,结果为0; 按位或,8|4,将8和4进行按位或运算,结果为12; 按位非,~8,将8进行按位非运算,结果为-9(一个字符占一个字节,即8个二进制位,最高位为1时为负数); 按位异或(a异或b,a、b值不同结果为1,相同结果为0),10^6,将10和6进行按二进制位异或运算,结果为12。 |
<<=、>>= | 将变量的值左移指定位数之后重新赋给该变量,x<<=3,将x的值左移3位,重新赋给变量x; 将变量的值右移指定位数之后重新赋给该变量,x>>=4,将变量x的值右移4位后重新赋给变量x。 |
&=、|=、^= | 将变量的值与指定的数值按位与之后重新赋给该变量,x&=8,将变量x的值与8按位与运算之后重新赋给变量x; 将变量的值与指定的数值按位或之后重新赋给该变量,x|=7,将变量x的值与7执行按位或运算之后重新赋给变量x; 将变量的值与指定的数值按位异或之后重新赋给该变量,x^=9,将变量x的值与9执行按位异或运算之后重新赋给变量x。 |
运算操作符与运算命令 | 意义 | 说明 |
---|---|---|
(()) | 用于整数运算的常用运算符 | 在(())中使用变量时可以去掉变量前的$符号 |
let | 用于整数运算 | 使用let命令可以执行一个或者多个算术表达式,其中的变量名毋需使用$符号 |
expr | 可用于整数运算,但还有很多其他的额外功能 | 使用expr时,运算符及用于计算的数字左右都至少有一个空格,否则会报错;使用乘号时,必须使用反斜线屏蔽其特定含义;使用expr做计算,将一个未知的变量和一个已知的整数相加,看返回码是否为0,如果为0就认为做加法的变量为整数,否则就不是整数。 |
bc | linux下的一个计算器程序(适合整数及小数运算) | [root@node13 test9]# echo `seq -s "+" 10` = `seq -s "+" 10 | bc ` seq是生成数字序列,-s是指定数字序列之间的分隔符 |
$[] | 用于整数运算 | |
awk | awk既可以用于整数运算,也可以用于小数运算 | |
declare | 定义变量值和属性,-i参数可以用于定义整形变量,做运算 |
- 示例1:
[root@node1 ~]# expr 1 + 1 #注意+左右必须要有空格
2
[root@node1 ~]# expr 1+1 #否则原样显示
1+1
[root@node1 ~]# a=1
[root@node1 ~]# b=2
[root@node1 ~]# expr $a + $b #支持变量
3
[root@node1 ~]# let num=1+2 #注意let中运算符左右不能有空格
[root@node1 ~]# echo $num
3
[root@node1 ~]# echo $((1+2))
3
[root@node1 ~]# echo $((3%5))
3
[root@node1 ~]# echo $((3 % 5)) #可以有空格
3
[root@node1 ~]# echo $((3.2-1)) #只支持整数运算
-bash: 3.2-1: syntax error: invalid arithmetic operator (error token is ".2-1")
[root@node1 ~]# echo $[2+3] #[] 等价于(()) 同样只支持整数运算
5
[root@node1 ~]# bc
bc 1.07.1
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006, 2008, 2012-2017 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
1.1+2
3.1
1.5>4
0
1.2<3
1
quit #退出交互模式
#不进入交互模式直接使用,bc需要放在最后
[root@node1 ~]# echo "s=3; 11/3" | bc
3
[root@node1 ~]# echo "scale=3; 11/3" | bc
3.666
#scale=3 表示保留运算精度
- 示例2:返回变量长度
[root@node1 ~]# str1="hello world"
[root@node1 ~]# echo ${#str1}
11
#变量截取
[root@node1 ~]# echo ${str1:0:3} #从左边第一个字符开始截取3个
hel
[root@node1 ~]# echo ${str1::3} #可以省略起始0
hel
[root@node1 ~]# echo ${str1:1} #从下标1开始截取到尾部
ello world
[root@node1 ~]# echo ${str1:0-1:1} #从右边第一个字符开始截取1个,左边第一个为0,右边第一个为0-1
d
[root@node1 ~]# echo ${str1: -5} #使用空格代替0,同上
world
[root@node1 ~]# echo ${str1:-5} #没有空格表示提取整串
hello world
[root@node1 ~]# echo ${str1:0-5} #从右边第5个开始截取到尾部
world
#使用%截取,删除右边字符,保留左边字符(怎么把文件的扩展名去掉方法)
[root@node1 ~]# filename=testfile.tar
[root@node1 ~]# file=${filename%.*} #%,表示从右边开始检索第一次出现,之后删除,右侧的内容,保留左边内容,用于对文件名去掉扩展名
[root@node1 ~]# echo $file
testfile
[root@node1 ~]# url=http://www.baidu.com/index.html
[root@node1 ~]# ul1=${url%.*}
[root@node1 ~]# echo $ul1
http://www.baidu.com/index
[root@node1 ~]# url=http://www.baidu.com/index.html
[root@node1 ~]# ul1=${url%%.*}
[root@node1 ~]# echo $ul1
http://www
[root@node1 ~]# ul1=${url%%:*} # %% :表示从右边开始检索最后一次出现的:之后删除:右侧的内容,保留左边内容
[root@node1 ~]# echo $ul1
http
[root@node1 ~]# url=http://www.baidu.com/index.html
[root@node1 ~]# ul1=${url#*.} # #*. 表示左向右遍历,删除第一次出现的.左侧内容
[root@node1 ~]# echo $ul1
baidu.com/index.html
[root@node1 ~]# url=http://www.baidu.com/index.html
[root@node1 ~]# ul1=${url%/*}
[root@node1 ~]# echo $ul1
http://www.baidu.com
[root@node1 ~]# url=http://www.baidu.com/index.html
[root@node1 ~]# ul1=${url##*/}
[root@node1 ~]# echo $ul1
index.html
表达式 | 说明 |
---|---|
${parameter} | 返回变量的内容 |
${#parameter} | 返回变量内容的长度(按字符) |
${paramater:offset} | 在变量${parameter}中,从位置offset之后开始提取子串到结尾 |
${paramater:offset:length} | 在变量${parameter}中,从位置offset之后开始提取长度为length的子串 |
${parameter#word} | 从变量${parameter}开头开始删除最短匹配的word子串 |
${parameter##word} | 从变量${parameter}开头开始删除最长匹配的word子串 |
${parameter%word} | 从变量${parameter}结尾开始删除最短匹配的word子串 |
${parameter%%word} | 从变量${parameter}结尾开始删除最长匹配的word子串 |
${parameter/pattern/string} | 使用string代替第一个匹配的pattern |
${parameter//pattern/string} | 使用string代替所有匹配的pattern |
- -示例:如果paramater变量值为空或为赋值,则返回word字符串的值,注意:paramater变量值不变
[root@node1 ~]# echo $var1
[root@node1 ~]# var2=${var:-hello}
[root@node1 ~]# echo $var2
hello
- -示例:如果parameter变量有值,则返回注意:parameter变量值不变
[root@node1 ~]# var1="hello"
[root@node1 ~]# echo $var1
hello
[root@node1 ~]# var2=${var1:-world}
[root@node1 ~]# echo $var2
hello
- =示例:如果parameter变量有值,则返回parameter变量值
[root@node1 ~]# var=hello
[root@node1 ~]# var2=${var1:=world}
[root@node1 ~]# echo $var2
world
- ?示例:如果paramater变量值为空或为赋值,则word字符串会作为标准错误输出,否则输出变量的值。
[root@node1 ~]# var2=${var1:?world}
-bash: var1: world #标志错误输出
[root@node1 ~]# var1=hello
[root@node1 ~]# var2=${var1:?world}
[root@node1 ~]# echo $var2
hello
- +示例:如果paramater变量值为空或为赋值,则什么都不做,否则word字符串
[root@node1 ~]# var1=hello
[root@node1 ~]# var2=${var1:+world}
[root@node1 ~]# echo $var2
world
[root@node1 ~]# echo $var1
hello
- 面试题:删除7天前的过期备份数据
#!/bin/bash
find ${path:-/tmp} -name "*.tar.gz" -type f mtime +7 | xargs rm -f
#find 为查找文件命令
#${path:-/tmp} :表示如果没有定义path则使用/tmp代替
#find不支持 | 使用xargs命令表示管道符进行数据转换