5、Shell变量
变量分类
变量可分为2类:环境变量(全局变量)和局部变量(本地变量)。
环境变量可以在创建他们的shell及其派生出来的任意子进程shell中使用。局部变量只能在创建他们的shell函数或脚本中使用。
命名规范:
一般是字母、数字、下划线组成,必须以字母开头。
语义要清晰,能够正确表达变量内容的含义,过长的英文单词可采用前几个字符代替。多个单词用"_"连接。
避免无含义的字符或数字。
环境变量
(1)概念
环境变量用于定义Shell的运行环境,保证Shell命令的正确执行。所有环境变量都是系统全局变量,可用于所有子进程中。
环境变量可以在命令行中设置,但用户退出时这些变量值也会丢失,因此,最好在用户home目录下的.bash_profile或.bashrc(非用户登录特有,例如SSH)文件中,或全局配置文件/etc/bashrc(非用户登录特有,例如SSH)或/ect/profile,在每次用户登录时将其初始化。
根据规范,所有环境变量应均为大写。在用于用户进程前,必须用export命令抛出。
使用习惯:一般数字不加引号,其它默认加双引号。
(2)查看系统所有环境变量
- a、env命令显示全局变量
[root@CentOS8 test]# env HOSTNAME=lamp TERM=linux SHELL=/bin/bash HISTSIZE=1000 …
- b、set输出所有的变量,包括全局变量和局部变量
[root@CentOS8 test]# set BASH=/bin/bash ... HISTCONTROL=ignoredups HISTFILE=/root/.bash_history HISTFILESIZE=1000 ...
(3)设置和临时生效自定义环境变量
如果想设置环境变量,就要在给变量赋值之后或在设置变量时使用export命令。
- a、export命令设置
变量名=value;
export 变量名
或
export 变量名=value
- b、declare -x命令设置,该命令单独使用会输出所有的变量、函数、整数和已经到处的变量
declare -x 变量名=value
- c、示例
[root@inspur ~]# abc=20 [root@inspur ~]# export abc [root@inspur ~]# export abd=30 [root@inspur ~]# declare -x abe=40
(4)永久生效自定义环境变量(别名alias也是一样)
- a、全局生效(所有用户)
vi /etc/profile 或 vi /etc/bashrc export abc = 20
- b、当前用户生效
vi ~/.bashrc 或 vi ~/.bash_profile export abc = 20
(5)设置登录提示的两种方式
- 1./etc/profile.d/方式定义环境变量
vi /etc/profile.d/test.sh echo "This is /etc/profile.d/test.sh...." export TEST_ABC=30 chmod +x /etc/profile.d/test.sh # 可执行权限 logout 回车登入 Last login: Sat May 6 11:53:28 2017 from 192.168.1.11 This is /etc/profile.d/test.sh.... [root@lamp ~]# echo $TEST_ABC 30
- 2.此种方法可以作为设置登录提示的方式,下面还有一种:
[root@CentOS8 ~]# cat /etc/motd
welcome to boxiaoyuan linux shell
对于用户的环境变量设置,比较常见的是用户家目录下的.bashrc和.bash_profile。
(6)显示和取消环境变量
- a、通过echo命令打印环境变量
[root@CentOS8 ~]# echo $HOME
/root
- b、通过printf命令打印环境变量(需在结尾加\n,显示的格式比echo丰富)
[root@CentOS8 ~]# printf "$HOME\n" /root
- c、通过unset命令取消环境变量(此时不要带$)
[root@CentOS8 ~]# echo $TEST_ABC 30 [root@CentOS8 ~]# unset TEST_ABC [root@CentOS8 ~]# echo $TEST_ABC [root@CentOS8 ~]#
(7)环境变量知识小结
- 变量名通常大写
- 变量可以在自身的Shell及子Shell中使用。
- 常用export来定义环境变量
- 执行env默认可以显示所有的环境变量名称及对应的值
- 输出时用“$变量名”,取消时用“unset 变量名”
- 书写crond定时任务时要注意,脚本要用到的环境变量最好先在所执行的Shell脚本中重新定义。
- 如果希望环境变量永久生效,可以将其放在用户环境变量文件或全局环境变量文件中。
(8)环境变量初始化与对应文件的生效顺序
系统运行Shell的方式有三种
- 1)系统用户登录后默认运行的Shell
- 2)非登录交互式运行Shell
- 3)执行脚本运行非交互式Shell
用户登录linux,shell会作为登录shell启动,此时shell加载环境变量的顺序:首先加载/etc/profile全局环境变量文件,然后执行/etc/profile.d目录下的脚本文件,之后开始运行$HOME/.bash_profile(用户环境变量文件),在这个文件中,又会去$HOME/.bashrc(用户环境变量文件),如果有,则执行,没有,则不执行,在$HOME/.bashrc文件中又会找/etc/bashrc(全局环境变量文件),如果有执行,如果没有,则不执行。
如果用户的Shell不是登录时启动(比如手动敲下bash时启动或其他不需要输入密码的登录及远程SSH连接),这种非登录Shell只会加载$HOME/.bashrc(用户环境变量文件),并会去找/etc/bashrc(全局环境变量文件)。因此如果希望在非登录shell下也可以读到设置的环境变量等内容,就需要将变量设定等写入$HOME/.bashrc或者/etc/bashrc。
局部变量
(1)概念
局部变量又称本地变量,只在用户当前的Shell生存期的脚本中使用。如果在Shell中启动另一个进程或退出,则本地变量的值将无效。
(2)定义局部变量
- a、普通字符串变量定义
变量名=value 变量名='value' 变量名="value"
- b、命令变量定义
变量名=``
变量名=$()
- c、函数中变量定义
local 变量名
local 变量名=value
一定要用local方式进行声明,使之只在本函数作用域内有效,防止变量在函数中命名与变量外部程序中变量重名,造成程序异常。
(3)示例1:命令行输入下列命令返加什么结果
[root@CentOS8 ~]# a=192.168.1.2 [root@CentOS8 ~]# b='192.168.1.2' [root@CentOS8 ~]# c="192.168.1.2" [root@CentOS8 ~]# echo "a=$b" a=192.168.1.2 [root@CentOS8 ~]# echo "c=${c}" c=192.168.1.2
提示:$c与${c}在这里是等同的
小结:将连接普通字符串的内容赋值给变量,打印变量时,是原样输出。
(4)示例2:命令行输入下列命令返加什么结果
[root@CentOS8 ~]# a=192.168.1.2 [root@CentOS8 ~]# a=192.168.1.2-$a [root@CentOS8 ~]# b='192.168.1.2-$a' [root@CentOS8 ~]# c="102.168.1.2-$a" [root@CentOS8 ~]# echo "a=$a" a=192.168.1.2-192.168.1.2 [root@CentOS8 ~]# echo "b=$b" b=192.168.1.2-$a [root@CentOS8 ~]# echo "c=${c}" c=102.168.1.2-192.168.1.2-192.168.1.2
小结:
单引号“'”是原样输出,不论引号内有什么,即使引号有变量,也会把变量名原样输出。适用于定义纯字符串。
双引号““”中的内容是会被解析的,将引号中的变量解析成该变量的内容结果输出。适用于字符串中附带有变量的内容的定义。
(5)单引号、双引号和无引号的区别
- (1)单引号
所见即所得,将单引号内的所有内容都不解析,原样输出。
- (2)双引号
输出双引号中的所有内容。如果引号中有命令(反引号中)、变量、特殊转义符等,就会先解析变量,将解析结果输出到最终内容中。
- (3)无引号
类似于双引号,把解析结果输出到最终内容中,但如果字符串中带有空格等特殊字符,则不能完整的输出,需要加上双引号。最好用双引号代替无引号。一般脚本中单纯的数字可以不加引号,普通字符串尽量用双引号。
- (4)注意事项
对某些语言不适合,如awk内部就有特殊规定(单、双引号正好与shell中相反)。
- (5)示例1:awk调用数据型shell变量
[root@CentOS8 ~]# ETT=123 [root@CentOS8 ~]# echo $ETT 123 [root@CentOS8 ~]# awk 'BEGIN {print "$ETT"}' $ETT [root@CentOS8 ~]# awk 'BEGIN {print '$ETT'}' 123 [root@CentOS8 ~]# awk 'BEGIN {print $ETT}' [root@CentOS8 ~]#
- (6)示例2:awk调用字符型shell变量
[root@CentOS8 ~]# ETT='abc' [root@CentOS8 ~]# echo $ETT abc [root@CentOS8 ~]# awk 'BEGIN {print "$ETT"}' $ETT [root@CentOS8 ~]# awk 'BEGIN {print '$ETT'}' [root@CentOS8 ~]# awk 'BEGIN {print $ETT}' [root@CentOS8 ~]# awk 'BEGIN {print "'$ETT'"}' abc
总结:不管变量如何定义,赋值,除了单引号以外,利用awk直接获取变量的输出,结果都是一样的,因此在awk取用shell变量时,我们更多的还是喜欢先用echo加符号输出变量,然后通过管道给awk,进而控制变量的输出结果。
[root@CentOS8 ~]# ETT="boxiaoyuan" [root@CentOS8 ~]# echo $ETT | awk '{print $0}' boxiaoyuan [root@CentOS8 ~]# echo "$ETT" | awk '{print $0}' boxiaoyuan [root@CentOS8 ~]# echo '$ETT' | awk '{print $0}' $ETT [root@CentOS8 ~]# ETT=`pwd` [root@CentOS8 ~]# echo $ETT | awk '{print $0}' /root [root@CentOS8 ~]# echo "$ETT" | awk '{print $0}' /root [root@CentOS8 ~]# echo '$ETT' | awk '{print $0}' $ETT
(6)把一个命令的结果作为变量的内容赋值方法
变量名=`ls` #<==把命令用反引号引起来,不推荐使用这种方法,因为容易和单引号混淆
变量名=$(ls) #<==把命令用$()括起来,推荐使用这种方法
[root@CentOS8 ~]# CMD=$(date +%F) #<==将当前日期赋值给CMD变量 [root@CentOS8 ~]# echo $CMD #<==输出变量的值 2020-08-22 [root@CentOS8 ~]# ECHO $(date +%F).tar.gz #<==直接输出时间命令的结果 bash: ECHO: command not found... Similar command is: 'echo' [root@CentOS8 ~]# echo $(date +%F).tar.gz 2020-08-22.tar.gz [root@CentOS8 ~]# echo `date +%F`.tar.gz 2020-08-22.tar.gz [root@CentOS8 ~]# tar zcf etc_$(date +%F).tar.gz /server/files tar: Removing leading `/' from member names [root@CentOS8 ~]# ls -l etc_2020-08-22.tar.gz -rw-r--r-- 1 root root 118 Aug 22 08:18 etc_2020-08-22.tar.gz [root@CentOS8 ~]# H=$(uname -n) [root@CentOS8 ~]# echo $H CentOS8 [root@CentOS8 ~]# tar zcvf $(uname -n).tar.gz /server/files/ #<==将主机作为压缩包 tar: Removing leading `/' from member names /server/files/ [root@CentOS8 ~]# ll CentOS8.tar.gz -rw-r--r-- 1 root root 118 Aug 22 08:19 CentOS8.tar.gz
(7)局部变量定义及赋值小结
如果变量内容为连续的数字或字符串,赋值时可以不加引号;如果内容多且有空格且想解析内容中的变量,就加双引号;如果想原样输出变量中内容,就加单引号。
(8)变量的输出方法小结
使用“$变量名”即可输出变量内容,常用“echo $变量名”的方式
$dbname_tname,当变量后面连接有其他字符发时候,必须给变量加上大括号{},如${dbname}_tname。
Shell特殊变量
(1)位置变量
- a、$0:获取当前执行的shell脚本的文件名,如果执行脚本带路径,则包含脚本路径。
(i)示例1
[root@CentOS8 ~]# cat 1.sh echo $0 [root@CentOS8 ~]# sh 1.sh 1.sh [root@CentOS8 ~]# sh $(pwd)/1.sh /root/1.sh
- b、$n:获取当前执行的shell脚本的第n个参数值,n>1,如果n>9时,则需用大括号括起来,如${10}。
(i)示例1
[root@CentOS8 ~]# cat 3.sh echo $1 [root@CentOS8 ~]# sh 3.sh [root@CentOS8 ~]# sh 3.sh aaa aaa [root@CentOS8 ~]# sh 3.sh aaa bbb aaa [root@CentOS8 ~]# sh 3.sh "aaa bbb" aaa bbb
(ii)示例2
[root@Centos8 ~]# echo $(echo -n 'echo $1 ' && echo '$'{2..15})>4.sh [root@inspur ~]# cat 4.sh echo $1 $2 $3 $4 $5 $6 $7 $8 $9 $10 $11 $12 $13 $14 $15 [root@Centos8 ~]# sh 4.sh {a..z} a b c d e f g h i a0 a1 a2 a3 a4 a5 [root@Centos8 ~]# echo $(echo 'echo $1' && echo '$'{2..9} && echo '${'{10..15}'}')>4.sh [root@Centos8 ~]# cat 4.sh echo $1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11} ${12} ${13} ${14} ${15} [root@Centos8 ~]# sh 4.sh {a..z} a b c d e f g h i j k l m n o
- c、$#:获取当前执行的shell命令行中参数的总个数。
(i)示例1
[root@CentOS8 test]# echo 'echo $#' > 5.sh [root@CentOS8 test]# cat 5.sh echo $# [root@CentOS8 test]# sh 5.sh {a..z} 26
(ii)示例2
[root@CentOS8 test]# cat 6.sh [ $# -ne 2 ] && { echo "must two" exit 1 } echo 'OK' [root@CentOS8 test]# sh 6.sh must two [root@CentOS8 test]# sh 6.sh aaa bbb OK [root@CentOS8 test]# sh 6.sh aaa bbb ccc must two
- d、$*:获取当前执行的shell的所有参数,但将命令行的所有参数视为一个字符串。相当于"$1$2$3.."。
- e、$@:获取当前执行的shell的所有参数,是将命令行的所有参数视为一个个的单个个体,以"$1" "$2" "$3" "$4"...形式获取,这是将参数传递给其它程序的最佳方式。
- f、$*与$@的区别
(i)示例1:有双引号
[root@CentOS8 test]# set -- "I am" handsome test <--#以set方式来模拟传入3个参数 [root@CentOS8 test]# echo $# 3 <--#当前共传入3个参数 [root@CentOS8 test]# for i in "$*";do echo $i;done I am handsome test <--#$*将3个参数视为1个参数 [root@CentOS8 test]# for i in "$@";do echo $i;done <--#$*3个参数还是认为是3个参数 I am handsome test
(ii)示例2:无引号($*与$@效果一样)
[root@CentOS8 test]# for i in $*;do echo $i;done I am <--#将第1个参数“I am”也折分了 handsome test [root@CentOS8 test]# for i in $@;do echo $i;done I am handsome test
常用的特殊位置参数变量说明
位置变量 | 作用说明 |
$0 | 获取当前执行的Shell脚本的文件名,如果执行脚本包含了路径,那么就包含脚本路径 |
$n | 获取当前执行的Shell脚本的第n个参数值,n=1..9,当n为0时表示脚本的文件名;当n大于9时,则用大括号括起来,例如${10},借的参数以空格隔开 |
$# | 获取当前执行的Shell脚本后面接的参数的总个数 |
$* | 获取当前Shell脚本所有传参的参数,不加引号和$a相同;如果$*加上双引号,例如:“$*”,则表示将所有的参数视为单个字符串,相当于”$1$2$3” |
$@ | 获取当前Shell脚本所有传参的参数,不加引号和$*相同;如果给$@加上双引号,例如:“$@”,则表示将所有的参数视为不同的独立字符串,相当于”$1””$2”…。这是将多参数传递给其他程序的最佳方式,以内它会保留所有的内嵌在每个参数里的任何空白。”$@”和”$*”都加双引号时,两者是有区别的;都不加双引号时,两者无区别。 |
(2)进程状态变量
Shell进程的特殊状态变量说明
位置变量 | 作用说明 |
$? | 获取执行上一个指令的执行状态返回值(0为成功,非零为失败),这个变量最常用 |
$$ | 获取当前执行的Shell脚本进程号(PID),这个变量不常用,了解即可 |
$! | 获取上一个在后台工作的进程的进程号(PID),这个变量不常用,了解即可 |
$_ | 获取在此之前执行的命令或脚本的最后一个参数,这个变量不常用,了解即可 |
Shell内置命令
常用的内部的命令:echo、eval、exec、export、read、shift。
- (1)echo将命令后面args指定的字符串及变量等显示到标准输出
- -n 不换行输出内容
- -e 解析转义字符(见下面)
- \n 换行 \r 回车 \t 制表符 \b 退格 \v 纵向制表符
[root@CentOS8 test]# echo boxiaoyuan;echo zhangsan boxiaoyuan zhangsan [root@CentOS8 test]# echo -n boxiaoyuan;echo shangzan boxiaoyuanshangzan [root@CentOS8 test]# echo "boxiaoyuan\tzhangsan" boxiaoyuan\tzhangsan [root@CentOS8 test]# echo -e "boxiaoyuan\tzhangsan" boxiaoyuan zhangsan
- (2)eval:当Shell程序执行到eval语句时,Shell读入参数args,并将他们组合成一个新的命令,然后执行。
- (3)exec:在不创建新的子进程的前提下,转去执行指定的命令,当指定的命令执行完毕后,该进程就终止了。
- (4)read从标准输入读取字符串等信息,传给Shell程序内部定义的变量
- (5)移动位置变量的命令shift
- a、说明:将后面的变量位置依次往前移动。不指定位移量的默认情况下每次前移1个位置。每执行一次shift命令,都会使所有位置的参数依次向左移动1个位置(默认),并使位置参数$#减1,直至0为止。
- 作用:就是方便。
- (6)exit 退出Shell程序,在shell之后可以有选择的制定一个数位作为返回状态。