Linux shell 局部变量与全局变量
Linux shell 局部变量与全局变量
bash 变量默认都是全局变量,脚本内、函数内都可以调用,无论在什么位置(函数体中也一样),即函数体外可以调用函数体内的变量
local一般用于局部变量声明,多在函数体内使用,不可以在函数外引用
如果要在函数体里设置局部变量,则要使用 local
1、全局变量
在脚本中定义的变量都是全局变量,包括脚本中的函数,函数中使用的变量依然在全局生效。
声明变量时,使用“declare”,可以通过选项在声明的时候给予某些属性。
declare 选项:
-p: -a:数组索引 -A:数组变量 -f:仅仅代表函数名 -i:整型数 -l:小写字母(自动转) -r:只读变量声明 -t:跟踪属性,调试用 -u:大些字母 -x:通过环境变量导出的变量声明
定义一个数组、赋值、输出数组:
declare -a arr arr=(1 8 3 6 12) echo ${#arr[@]} # 5 echo ${arr[@]} # 1 8 3 6 12 echo ${arr[*]} # 1 8 3 6 12
2、局部变量
通过“local”关键字在函数内部定义局部变量,可以接受“declare”的一切选项,最好在函数中定义局部变量时使用。
当在函数中使用local时,它会导致变量名使可见范围仅限于该函数及其子函数。
示例代码:
vim test_shell_var.sh
#!/bin/bash # # mimvp.com # 2018-09-18 mimvp='macro_blog' function test_macro() { echo $mimvp # macro_blog mimvp='macro_proxy' echo $mimvp # macro_proxy } test_macro echo $mimvp # macro_proxy mimvp='local_blog' function test_local() { echo $mimvp # local_blog local mimvp mimvp='local_proxy' echo $mimvp # local_proxy } test_local echo $mimvp # local_blog
运行结果:
macro_blog
macro_proxy
macro_proxy
local_blog
local_proxy
local_blog
变量类型:全局变量(环境变量)和局部变量(本地变量)
全局变量(环境变量),可以在定义它们的shell及其派生出来的任意子进程的shell中使用。
局部变量(本地变量),只能在定义它们的函数/脚本内部中使用。
还有一些变量是用户创建的,其他的则是专用的shell变量。
1、全局变量(环境变量)
全局变量(环境变量)可用于定义shell的运行环境,环境变量可以在配置文件中定义与修改,也可以在命令行中进行临时设置,但是命令行中的修改操作在终端重启时就会丢失,因此最好在配置文件中修改:"~/.bash_profile" 文件,或者全局配置 "/etc/profile"、"/etc/bashrc"、"/etc/profile.d"文件中定义。将环境变量放在profile文件中,每次用户登录时这些变量值将被初始化。比如HOME、USER、SHELL、UID等再用户登录之前就已经被/bin/login程序设置好了。
常见系统环境变量:
TMOUT:设置自动退出的误操作等待时间,如 18000
HOSTTYPE:系统文件系统类型,如 x86_64
HISTSIZE:历史命令记录条数,如 1000
HOME:用户登录时进入的目录,如 /root
UID:当前用户的id,如 0 (root)
SHELL:当前shell解释器,如 /bin/bash
PWD:当前所在路径(每改变一次目录,该值就会改变) ,如 /home/mimvp , /root
PATH:可执行文件默认路径,如 /sbin:/bin:/usr/sbin:/usr/bin
可以用echo来显示查看全局变量,例如:
echo $HOME # /home/mimvp
echo $PATH # /usr/local/php/bin:/usr/local/httpd/bin:/home/java/jdk-10.0.2/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
env(或printenv)、set 也可以用来查看系统的环境变量,但不是单独查看,例如:
[root@mimvp-sz2 script]# env XDG_SESSION_ID=11843 HOSTNAME=mimvp-sz2 TERM=xterm SHELL=/bin/bash HISTSIZE=1000 SSH_CLIENT=106.39.148.162 23363 22 NGINX_ROOT=/usr/local/nginx QTDIR=/usr/lib64/qt-3.3 QTINC=/usr/lib64/qt-3.3/include SSH_TTY=/dev/pts/1 .....
自定义环境变量,采用 export:
1)export 变量名=value
2)变量名=value;export 变量名
3)declare -x 变量名=value
命令行的三种方式测试如下:
# export TEST='mimvp.com' # echo $TEST mimvp.com # # TEST2='www.mimvp.com'; export TEST2 # echo $TEST2 www.mimvp.com # # declare -x TEST3='blog.mimvp.com' You have new mail in /var/spool/mail/root # echo $TEST3 blog.mimvp.com # # env | grep -i "TEST" TEST=mimvp.com TEST2=www.mimvp.com TEST3=blog.mimvp.com
临时取消环境变量用 unset
例如:unset USER,要永久生效还是要写到配置文件中
# unset TEST [root@mimvp-sz2 script]# env | grep -i "TEST" TEST2=www.mimvp.com TEST3=blog.mimvp.com
这里依旧是临时生效,在shell终端关闭后就消失了,若要永久生效,就需要写到配置文件中
注意:写到配置文件中后,需要运行一遍配置文件的脚本才可生效,或重启服务器后生效
2、局部变量(本地变量)
本地变量在用户当前的shell生存期的脚本中使用。
在一个函数中将某个变量声明为local,则该变量就是一个局部变量,只在本函数中有效。
定义:
变量名=value
变量名='value'
变量名="value"
shell中变量名的要求:一般遵循字母、数字、下滑线组成,不能以数字开头
以下脚本执行后(交互式非交互式都可以测试)输出什么(c与c与{c}等同)?
a=192.168.1.1 b='192.168.1.2' c="192.168.1.3" echo "A=$a" echo "B=$b" echo "C=${c}" a=192.168.1.1-$b b='192.168.1.2-$b' c="192.168.1.3-$b" echo "A=$a" echo "B=$b" echo "C=${c}"
输出结果如下:
A=192.168.1.1 B=192.168.1.2 C=192.168.1.3 A=192.168.1.1-192.168.1.2 B=192.168.1.2-$b C=192.168.1.3-192.168.1.2-$b
总结分析:
单引号与双引号的区别在于:
1)单引号内若存在变量,存在的变量当做字符串不会被解析,原样输出;
2)双引号中若存在变量,该变量会被解析出其具体的值再加入到字符串中。
① 不加引号可以直接定义内容包含数字、字符串、路径名等,适合定义数字
② 单引号适合于纯定义字符串,
③ 双引号适合于字符串的内容中包含有变量的内容的定义。
使用习惯:
数字以及不带空格的简单字符串不加引号,其它长的特别是有空格的字符串加双引号;
遇到“$变量名”,但不想解析的加单引号,但一般出现$
都是为了解析变量,所以单引号较少使用
注意:单引号与双引号的特点不具有普遍性,如下:
在普通shell中:
ETT=123 echo '$ETT' # 打印$ETT(单引号不解析) echo "$ETT" # 打印123(双引号解析)
而在awk中调用shell变量:
awk 'BEGIN {print '$ETT'}' # 打印123(单引号解析) awk 'BEGIN {print "$ETT"}' # 打印$ETT(双引号不解析)
虽然在awk中不具有普遍性,但是在普通Shell中还是具有普遍性的。
3、局部变量的其它问题
用反引号将命令的结果作为变量名是常用的方法:cmd=`date +%F`
用$符号将命令的结果作为变量名也比较常用:cmd=$(date +%F)
变量在大括号上的使用:在以时间、主机名等为包名一部分来打包的时候常用
用时间作为文件名的一部分打包
cmd=$(date +%F) # 由于`date +%F`的反引号不容易辨认,就不太使用`date +%F` tar -zcf code_$(date+ %F)_mimvp.tar.gz /etc/passwd # 没有问题 tar -zcf code_`date +%F`_mimvp.tar.gz /etc/passwd # 没有问题 tar -zcf code_mimvp_$cmd.tar.gz /etc/passwd # 没有问题 tar -zcf code_${cmd}_mimvp.tar.gz /etc/passwd # 不会有歧义 tar -zcf code_$cmd_mimvp.tar.gz /etc/passwd # 会有歧义,不清楚是应该解析$cmd还是cmd_mimvp
上面命令,对后两个命令的测试结果如下:
# cmd=$(date +%F) # echo $cmd 2018-09-20 # # tar -zcPf code_${cmd}_mimvp.tar.gz /etc/passwd # ll code* -rw-r--r-- 1 root root 846 Sep 20 14:28 code_2018-09-20_mimvp.tar.gz # # tar -zcPf code_$cmd_mimvp.tar.gz /etc/passwd # ll code* -rw-r--r-- 1 root root 846 Sep 20 14:28 code_2018-09-20_mimvp.tar.gz -rw-r--r-- 1 root root 846 Sep 20 14:29 code_.tar.gz
说明:不加{} 会产生歧义,不知道变量是 $cmd 或 $cmd_mimvp,所以最后为空
用主机名与时间打包
cmd=$(date +%F) host=$(hostname) tar -zcf code_${cmd}_${host}.tar.gz /etc/passwd
运行日志:
# echo $cmd 2018-09-20 # host=$(hostname) # echo $host mimvp-sz2 # tar -zcPf code_${cmd}_${host}.tar.gz /etc/passwd # ll code* -rw-r--r-- 1 root root 846 Sep 20 14:34 code_2018-09-20_mimvp-sz2.tar.gz
小结:编写shell脚本,养成将字符串变量用{}括起来使用的好习惯,防止不易发现的歧义或错误
4、Shell 特殊变量
$0 $n $# $* $@ $$ $? $_ $0:获取脚本的路径 $n:获取当前执行shell脚本的第n个参数, 例如第一个参数 $1 $#:获取当前shell命令行中参数的总个数, 格式 ${#arr[*]} $*:获取当前执行的shell的所有参数,将所有的命令行参数视为一个字符串:"$1$2$3..." $@:获取当前执行的shell的所有参数,将所有的命令行的每个参数视为单个的字符串:"$1" "$2" "$3" $$:获取当前的shell进程号 $?:获取执行上一个指令的返回值,0为成功,非零为失败,可以对上一个命令执行是否成功进行判断 $_:在此之前执行的命令或脚本的最后一个参数
$0:获取脚本的路径,获取当前执行的shell脚本的文件名,执行时给定的是完整路径则获取到的也是完整路径
两个命令与$0的组合测试:获取一个带路径的文件的路径名与文件名两部分
dirname:获取目录名
basename:获取文件名
测试 $0:
vim test.sh
#!/bin/bash # # mimvp.com # 2019.09.18 dirname $0 basename $0
运行 test.sh
# sh test.sh . test.sh # # sh `pwd`/test.sh /root/script test.sh
说明:圆点 . 表示当前目录,`pwd`表示获取当前目录的绝对路径
$n:获取当前执行的shell脚本的第n个参数,
1)如果n=0则获取的是脚本的文件名
2)如果n>9则需要用大括号括起来,例如:${21}
测试 $n:
# seq 9 | sed 's#[0-9]#echo $&#g' > test2.sh # cat test2.sh echo $1 echo $2 echo $3 echo $4 echo $5 echo $6 echo $7 echo $8 echo $9 # sh test2.sh `seq -s " " 10` 1 2 3 4 5 6 7 8 9
说明:
seq 9 是表示生成一个数组
# seq 9 1 2 3 4 5 6 7 8 9
sed 's#[0-9]#echo $&#g' 是表示把每一行的匹配的数字,替换为字符串 echo $[0-9] 对应的数字
# echo 5 | sed 's#[0-9]#echo $&#g' echo $5
更多$参数:
$# :获取当前shell命令行中参数的总个数
$* :获取当前执行的shell的所有参数,将所有的命令行参数视为一个字符串 :"$1$2$3..."
$@ :获取当前执行的shell的所有参数,将所有的命令行的每个参数视为单个的字符串 :"$1" "$2" "$3" ... 这是将参数传递给其它程序的最佳方式,因为它会保留所有内嵌在每个参数里的任何空格分隔
测试$ 如下:
vim test3.sh
#!/bin/bash # # mimvp.com # 2018-09-18 echo $# echo $* echo $@
运行日志:
# sh test3.sh `seq -s " " 10`
10
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
获取状态变量:
$$:获取当前的shell进程号
$?:获取执行上一个指令的返回值,0为成功,非零为失败,可以对上一个命令执行是否成功进行判断
$_:在此之前执行的命令或脚本的最后一个参数
$?变量其实获取的是上一个程序返回给父进程shell的返回值(该值在0-255之间:0表示运行成功,2表示权限拒绝,1~125为运行失败原因是脚本命令、系统命令错误或参数传递错误,126为找到该命令但是无法执行,127为无该命令/程序,>128表示命令被系统强制结束)
$?的不同返回值测试:
# tar -zcPf code_$(date +%F).tar.gz /etc/passwd # echo $? 0 # tar -zcPf code_$(date +%F).tar.gz /etc/ ^C 【Ctrl+C 迅速中断执行】 # echo $? 130
说明:成功执行压缩命令,返回 $? 为 0(成功);中断执行命令,返回错误码 130
5、Shell 脚本追踪
检查脚本语法、调试执行脚本
$ bash -n tmp_shell.sh $ bash -x tmp_shell.sh + declare -a arr + arr=(1 8 3 6 12) + echo 5 5 + echo 1 8 3 6 12 1 8 3 6 12 + echo 1 8 3 6 12 1 8 3 6 12
shell 脚本追踪
在测试脚本时,可以使用set命令进行运行时的追踪,在脚本中加入一行“set -x”;以“+”开头的行,就是获得的追踪内容(程序的执行过程)
vim tmp_shell.sh
#!/bin/bash # mimvp.com 2016.05.10 set -x declare -a arr arr=(1 8 3 6 12) echo ${#arr[@]} # 5 echo ${arr[@]} # 1 8 3 6 12 echo ${arr[*]} # 1 8 3 6 12
运行结果:
$ sh tmp_shell.sh + declare -a arr + arr=(1 8 3 6 12) + echo 5 5 + echo 1 8 3 6 12 1 8 3 6 12 + echo 1 8 3 6 12 1 8 3 6 12
说明:脚本文件内添加 set -x 与脚本执行时 bash +x tmp_shell.sh 效果是一样的,推荐使用 bash +x tmp_shell.sh
参考推荐:
Linux shell 脚本通过expect实现自动输入密码
nohup、&、disown、setsid、screen、jobs 后台运行命令区别
版权所有: 本文系米扑博客原创、转载、摘录,或修订后发表,最后更新于 2019-02-09 10:49:15
侵权处理: 本个人博客,不盈利,若侵犯了您的作品权,请联系博主删除,莫恶意,索钱财,感谢!