Shell脚本变量用法详解
变量基础
变量是命名的内存空间,用于存储数据并通过变量名引用。Shell脚本中变量无需声明类型,直接赋值即可使用。
变量类型
Shell变量主要分为以下几类:
-
内置变量
由系统预定义,如
PS1
(提示符格式)、PATH
(命令搜索路径)、UID
(用户ID)、HOSTNAME
(主机名)、HISTSIZE
(历史记录条数)。 -
用户自定义变量
由用户根据需求定义,命名需遵循规则(见下文)。
-
预定义变量
如
$?
(上一条命令的退出状态)、$$
(当前进程PID)、$0
(脚本名称)。 -
位置变量
通过
$1
、$2
...$9
引用脚本参数,${10}
及以上需加大括号。
变量数据类型
Shell变量默认存储字符串,数值实际为字符串形式。
- 字符串 :如
name="John"
。 - 数值 :仅支持整数运算,浮点数需借助外部工具(如
bc
)。
变量命名规则
- 禁止使用保留字(如
if
、for
)。 - 仅允许字母、数字、下划线,且不能以数字开头。
- 推荐命名规范:
- 普通变量:小写驼峰式(如
userName
)。 - 环境变量:全大写加下划线(如
LOG_PATH
)。 - 局部变量:函数内使用,前缀加
local
(如local count=1
)。
- 普通变量:小写驼峰式(如
变量定义与引用
-
定义普通变量
var_name="value" # 等号两侧无空格
引用变量时需加
$
:echo $var_name # 或 echo ${var_name} # 推荐,避免歧义
-
环境变量
通过
export
提升为环境变量,子进程可继承:export PATH=$PATH:/new/path
-
本地变量(函数内)
使用
local
声明,作用域限于函数:function demo() { local local_var="value" echo $local_var }
变量作用域
- 普通变量:仅当前Shell进程有效。
- 环境变量:当前Shell及其子进程有效。
- 本地变量:仅限函数内部。
示例:数值运算
Shell中整数运算需用 $(( ))
或 let
:
sum=$(( 1 + 2 )) # 输出3
let "sum *= 2" # 输出6
浮点数运算需借助 bc
:
echo "scale=2; 3/2" | bc # 输出1.50
注意事项
- 未定义的变量引用视为空字符串。
- 变量赋值时避免空格:
var=value
正确,var = value
错误。 - 引号使用:
- 双引号允许变量扩展:
echo "$var"
。 - 单引号禁止扩展:
echo '$var'
输出字面量$var
。
- 双引号允许变量扩展:
变量赋值:
name="value"
value可以是以下多种类型
直接字符串:name='root'
变量引用:name="$USER"
命令应用:name=`command` || name=$(command)
通配符:FILE=/etc/* /*表示etc目录下所有的文件名*/
注意:变量赋值是临时生效,当退出终端后,变量会自动删除,无法持久保存。
变量引用:
$name
${name}
弱引用和强引用:
-
"$name":弱引用,其中的变量引用会被替换成为变量值
-
'$name':强引用,其中的变量引用不会被替换成变量值,而保持原字符串
实例:
[root@localhost ~]# name='Mike' [root@localhost ~]# NAME="$USER" [root@localhost ~]# hostname=`hostname` [root@localhost ~]# echo "My name is $name" My name is Mike [root@localhost ~]# echo "My name is $NAME" My name is root [root@localhost ~]# echo "My hostname is $hostname" My hostname is localhost.localdomain [root@localhost ~]# [root@localhost ~]# NUM=`seq 10` [root@localhost ~]# echo $NUM 1 2 3 4 5 6 7 8 9 10 [root@localhost ~]# echo "$NUM" 1 2 3 4 5 6 7 8 9 10 [root@localhost ~]#
查看已定义的所有变量:
[root@localhost ~]#set
删除变量
[root@localhost ~]#unset shellname1 shellname2
//实例:
[root@localhost ~]# echo $name
Mike
[root@localhost ~]# unset name
[root@localhost ~]# echo $name
[root@localhost ~]#
实例:"{}"的使用
[root@localhost ~]# NAME=mage
[root@localhost ~]# AGE=20
[root@localhost ~]# echo $NAME
mage
[root@localhost ~]# echo $AGE
20
[root@localhost ~]# echo $NAME $AGE
mage 20
[root@localhost ~]# echo $NAME_$AGE
20
[root@localhost ~]# echo ${NAME}_$AGE
mage_20
[root@localhost ~]#
显示系统信息
[root@localhost ~]# vim shell/OS.sh
#!/bin/bash
RED="\E[1;31m"
GREEB="\E[1;32m"
END="\E[0m"
echo -e "\E[1;32m----------Host systeminfo----------$END"
echo -e "HOSTNAME: $RED `hostname`$END"
echo -e "IPADDR: $RED `ifconfig ens160 | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -n1` $END"
echo -e "OSVERSION: $RED `cat /etc/redhat-release`$END"
echo -e "KERNEL: $RED `uname -r`$END"
echo -e "CPU: $RED `lscpu | grep "型号名称:" | tr -s ' ' ' '|cut -d ' ' -f 2-5` $END"
echo -e "MEMORY: $RED `free -h | grep Mem | tr -s ' ' ' '|cut -d ' ' -f 4` $END"
echo -e "DISK: $RED `lsblk | grep '^sda' | tr -s ' ' | cut -d ' ' -f 4` $END"
echo -e "\E[1;32m---------- END ----------$END"
[root@localhost ~]# chmod +x shell/OS.sh
[root@localhost shellScript]# ./os.sh
----------Host systeminfo----------
HOSTNAME: localhost.localdomain
IPADDR: 192.168.115.213
OSVERSION: CentOS Linux release 7.9.2009 (Core)
KERNEL: 3.10.0-1160.el7.x86_64
CPU: 11th Gen Intel(R) Core(TM)
MEMORY: 1.0G
DISK: 200G
---------- END ----------
利用变量实现动态命令
[root@localhost ~]# CMD=hostname
[root@localhost ~]# $CMD
localhost.localdomain
[root@localhost ~]#
环境变量特性:
-
继承性:子进程(包括孙子进程)可以继承父进程的环境变量,但父进程无法获取子进程修改后的变量值
-
单向传递:当子进程修改继承的环境变量时,新的值会传递给后续的子进程(孙子进程等)
使用场景:
- 主要用于配置文件
- 脚本中较少使用
课程对比: 普通变量与环境变量的作用域差异
[root@localhost ~]# vim shell/father.sh
#!/bin/bash
NAME=father
echo "father.sh:NAME=$NAME"
echo "fatther is PID=$BASHPID"
shell/son.sh
[root@localhost ~]#vim shell/son.sh
#!/bin/bash
echo "son.sh:NAME=$NAME"
NAME=son
echo "son.sh:NAME=$NAME"
echo "son.sh PID is $BASHPID"
echo "son.sh father pid is $PPID"
sleep 100
[root@localhost ~]#chmod -R +x shell/*
[root@localhost ~]# ./shell/father.sh
father.sh:NAME=father
fatther is PID=12053
son.sh:NAME=
son.sh:NAME=son
son.sh PID is 12054
son.sh father pid is 12053
#子进程无法使用父进程的变量,需要自己赋值
变量声明和赋值:
export name=VALUE
declare -x name =VALUE
#或者先赋值再声明~
value可以是以下多种类型
直接字符串:name='root'
变量引用:name="$USER"
命令应用:name=`command` || name=$(command)
通配符:FILE=/etc/* /*表示etc目录下所有的文件名*/
declare 命令详解
declare
是 shell 内置命令,具有以下功能:
- 变量声明:第一种语法用于声明变量并设置其属性(如 [rix] 等)
- 函数显示:第二种语法用于显示 shell 函数信息
当不带任何参数执行时,declare
会显示当前环境中的所有 shell 变量和函数(效果等同于 set
命令)
declare [+/-][rxi][变量名称=设置值] 或 declare -f
参数说明:
+/- "-"可用来指定变量的属性,"+"则是取消变量所设的属性。
-f 仅显示函数
r 将变量声明为只读变量。注意,一旦设置为只读变量,既不能修改变量的值也不能删除变量,甚至不能通过+r取消只 读属性
x 指定的变量会成为环境变量,可供shell以外的程序来使用
i 将变量声明为整数型(integer)
p 显示指定变量的被声明类型。
1.声明整数型变量
#!/bin/bash
declare -i ab //声明整数型变量
ab=56 //改变变量内容
echo $ab //显示变量内容
--->
56
2.改变变量属性
#!/bin/bash
#声明整数型变量
declare -i ef
ef=1
echo $ef
#变量赋值,赋予文本值
ef="wer"
echo $ef
#取消变量属性
declare +i ef
ef="wer"
echo $ef
3.设置变量只读
#!/bin/bash
declare -r ab #设置变量为只读
ab=88 #改变变量内容
echo $ab #显示变量内容
--->
declare.sh:行3: ab: 只读变量
4.声明数组变量
#!/bin/bash
#声明数组变量
declare -a cd
cd[0]=a
cd[1]=b
cd[2]=c
#输出数组的指定内容
echo ${cd[1]}
#显示整个数组变量内容
echo ${cd[@]}
5.显示函数
#!/bin/bash
#声明函数
declare -f
function command_not_found_handle(){
if [ -x /usr/lib/command_not_found_handle ];then
/usr/bin/python /usr/lib/command_not_found_handle -- $1;
return $?;
else
if [ -x /usr/share/command_not_found_handle ];then
/usr/bin/python /usr/share/command_not_found_handle --$1;
return $?;
else
return 127;
fi;
fi;
}
变量引用:
$name
${name}
完善课程导入:
[root@localhost ~]# vim shell/father.sh
#!/bin/bash
NAME=father
export NAME
echo "father.sh:NAME=$NAME"
echo "fatther is PID=$BASHPID"
shell/son.sh
[root@localhost ~]#vim shell/son.sh
#!/bin/bash
echo "son.sh:NAME=$NAME"
NAME=son
echo "son.sh:NAME=$NAME"
echo "son.sh PID is $BASHPID"
echo "son.sh father pid is $PPID"
sleep 100
[root@localhost ~]#chmod -R +x shell/*
[root@localhost ~]# ./shell/father.sh
father.sh:NAME=father
fatther is PID=12053
son.sh:NAME=father
son.sh:NAME=son
son.sh PID is 12054
son.sh father pid is 12053
[root@localhost ~]# ./shell/father.sh
father.sh:NAME=father
fatther is PID=12142
son.sh:NAME=father
son.sh:NAME=son
son.sh PID is 12143
son.sh father pid is 12142
#父进程定义了一个环境变量,在子进程上可以进行调用
#父进程无法使用子进程的变量
#子进程自己定义了一个同名变量,就覆盖环境变量
显示所有环境变量:
[root@localhost ~]# env
[root@localhost ~]# printenv
[root@localhost ~]# export
[root@localhost ~]# declare -x
删除变量
[root@localhost ~]#unset shellname1 shellname2
Bash内建的环境变量
PATH
SHELL
USER
UID
PWD
SHLVL #shell的嵌套层数,即深度
LANG
MAIL
HOSTNAME
HISTSIZE
_ #下划线,表示前一命令的最后一个参数
只读变量
只读变量:只能声明定义,但后续不能修改和删除
声明只读变量:
readonly name
declare -r name
查看只读变量:
readonly [-p]
declare -r
位置变量
位置变量:在Bash Shell中内置的变量,在脚本代码中调用命令行传递给脚本的参数
$1,$2,... 对应第一个,第二个等参数,shift[n]换位置,最多9个
#预定义变量
$0 命令本身,包括路径
$* 传递给脚本的所有参数,全部参数合成一个字符串
$@ 传递给脚本的所有参数,每个参数为独立字符串
$# 传递给脚本的参数的个数
$? 上个命令的退出状态,或函数的返回值
$$ 当前shell进程ID。对于Shell脚本,就是这些脚本所在的进程ID
注意:$@,$*只有被双引号括起来的时候才会有差异
清空所有位置变量
set --
//写在脚本内部
实例演示1:
[root@localhost ~]# vim shell/ARG.sh
#!/bin/bash
echo "1st arg is $1"
echo "2st arg is $2"
echo "3st arg is $3"
echo "4st arg is $4"
echo "The number of are is $#"
echo "All args are $*"
echo "All args are $@"
echo "The scriptname is `basename $0`"
[root@localhost ~]# chmod +x shell/ARG.sh
[root@localhost ~]# shell/ARG.sh {1..10}
1st arg is 1
2st arg is 2
3st arg is 3
4st arg is 4
The number of are is 10
All args are 1 2 3 4 5 6 7 8 9 10
All args are 1 2 3 4 5 6 7 8 9 10
The scriptname is ARG.sh
[root@localhost ~]#
实例演示2:编写一个移动文件脚本
[root@localhost ~]# vim shell/move.sh
#!/bin/bash
WANGING_COLOR="echo -e \E[1;31m"
END="\E[0m"
DIR=/tmp/`date +%F_%H-%M-%S`
mkdir $DIR
mv $* $DIR
${WANGING_COLOR} MOVE $* to $DIR $END
[root@localhost ~]# chmod +x shell/move.sh
[root@localhost ~]# touch {a,b,c}
[root@localhost ~]# ls
a anaconda-ks.cfg b c shell
[root@localhost ~]# shell/move.sh a b c
MOVE a b c to /tmp/2022-08-16_10-07-55
[root@localhost ~]# tree /tmp/
/tmp/
└── 2022-08-16_10-07-55
├── a
├── b
└── c
1 directory, 3 files
[root@localhost ~]#
退出状态码变量
进程执行后,将使用变量 ? 保存状态码的相关数字,不同的值反应成功与失败, ?保存状态码的相关数字,不同的值反应成功与失败, ?保存状态码的相关数字,不同的值反应成功与失败,的取值范围为[0,255]
$?的值为0 代表成功
$?的值不为0 代表失败
用户可以在脚本中使用以下命令自定义退出状态码
exit [n]
实例:
[root@localhost ~]# ping -c 2 www.baidu.com > /dev/null
[root@localhost ~]# echo $?
0
[root@localhost ~]# cmd
-bash: cmd: 未找到命令
[root@localhost ~]# echo $?
127
[root@localhost ~]#
注意:
-
脚本中一旦遇到了exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字
-
如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码
实例1:$?获取上一个命令的退出状态
#!/bin/bash if [ "$1"==100 ];then exit 0 #参数正确,退出状态为0 else exit 1 #参数错误,退出状态为1 fi
exit
表示退出当前 Shell 进程,我们必须在新进程中运行 test.sh,否则当前 Shell 会话(终端窗口)会被关闭,我们就无法取得它的退出状态了
[root@localhost re_study]# bash test.sh 100
[root@localhost re_study]# echo $?
0
[root@localhost re_study]# bash test.sh 99
[root@localhost re_study]# echo $?
1
[root@localhost re_study]#
实例2:$?获取函数的返回值
#!/bin/bash
#得到两个数相加的和
function add(){
return `expr $1 + $2`
}
add 23 50 #调用函数
echo $? #获取函数返回值
# 运行结果:
[root@localhost re_study]# bash test.sh
73