二、变量
变量是在程序中保存用户数据的一段内存存储空间,变量对应的内存存储空间的内容是可以变化的。可以变化的是变量的值,变量的名字是不变的。
2.1 变量的定义与取消
变量的命名规范:只允许使用数字 、字母 和下划线,不能用数字开头。
变量赋值规范:变量名和变量的值之间用=连接,且等号左右不能有空格,如果变量的值含有空格字符,那么需要用引号引起来。
变量的类型:shell是一种动态类型语言和弱类型语言,变量是不分数据类型的,统一都使用字符串存储,但根据变量的上下文环境,允许程序执行一些不同的操作,如:比较、整数加减。
-
在命令行中定义:退出当前进程后该变量则失效
-
方式1:直接赋值,字符串建议使用引号引起来,尤其是字符串中有空格时必须使用单引号或者双引号引起来。
bash[root@server ~]# var1="test" -
方式2:通过执行命令,将命令的结果赋值给变量。
bash#语法: var2="`command`" var2=$(command) #示例 [root@server ~]# var2="`date`" [root@server ~]# var2=$(date) -
方式3:让用户在命令行交互式给变量赋值。
bash[root@server ~]# read -p "please input your var's value:" var3 var4 #在提示后面输入var3和var4变量的值 please input your var's value:123 abcd
-
-
在文件中定义:永久生效,将命令行中定义变量的语句写入文件中,系统启动后用户登录系统后会自动加载文件。
-
当前用户可读取的变量,将变量定义在用户家目录下的对应文件中
shell~/.bashrc和~/.bash_profile -
所有用户可读取的变量,将变量定义在/etc目录下的对应文件中
shell/etc/bashrc,/etc/profile /etc/profile.d/*.sh
-
注意:su和su -切换用户时加载的文件不一样,所以具体在哪个文件中定义变量,请根据实际情况选择。
-
位置参数变量
许多情况下,Shell脚本都需要接收用户的输入,根据用户输入的参数来执行不同的操作。
从命令行传递给Shell脚本的参数又称为位置参数,Shell脚本会根据参数的位置使用不同的位置参数变量读取它们的值。
bash$0 : 脚本文件名 $1-$9 : 1-9个参数 ${10} :10以上的参数需要大花括号括起 $* : 所有参数,把所有位置参数当成一个整体,如果有两个位置参数,"$*"相当于"$1 $2" $@ : 所有参数,把所有位置参数当成一个单独的字段,如果有两个参数,则"$@"相当于"$1" "$2" $# : 参数个数 $$ : 当前进程的PID $? : 上一个命令的返回值状态码,0为成功bash[root@server ~]# vim /shell/test2.sh #!/bin/bash echo "第2个位置参数是: $2" echo "第1个位置参数是: $1" echo "第4个位置参数是: $4" echo "所有参数是: $*" echo "所有参数是: $@" echo "参数的个数是: $#" echo "当前进程的PID值: $$" [root@server ~]# bash /shell/test2.sh 10 2 3 679 9 第2个位置参数是: 2 第1个位置参数是: 10 第4个位置参数是: 679 所有参数是: 10 2 3 679 9 所有参数是: 10 2 3 679 9 参数的个数是: 5 当前进程的PID值: 35977 -
取消变量
bash
[root@server ~]# unset var1 var2
2.2变量的使用
1、将变量的值输出到屏幕上
-
方式一:echo命令查看变量的值
bash[root@server ~]# var=hello [root@server ~]# var1=world [root@server ~]# echo $var ${var1} ${var}1 hello world hello1 -
方式二:printf命令查看变量的值
bash[root@server ~]# printf "$var ${var1} ${var}1" hello world hello1[root@server ~]# [root@server ~]# printf "$var ${var1} ${var}1\n" hello world hello1 -
方式三:查看一些变量的值
bashset:查看所有变量 declare:输出所有的变量以及函数 env:显示所有全局变量
!IMPORTANT
注意:单引号'',双引号"",\反斜线,反单引号``,$符号的作用
单引号'':所有单引号内的字符全部失去特殊含义。
双引号"":除\反斜线,反单引号``,$符号外的其它字符全部失去特殊含义。
\反斜线:反斜线后的第一个字符失效。
反单引号``:反单引号的中的字符会被识别成linux的命令执行。
$:读取变量。
2、如果变量里面的值是整数,我们可以对变量做运算(±*/%等)
bash
var1=1
var=2
echo $((var1+var2))
echo $[var1+var2]
let var3=var1+var2;echo $var3
expr $var1 + $var2
echo $var1+$var2 | bc
awk 'BEGIN {a=1;b=3;print a+b}'
declare -i var3=$var1+$var2;echo $var3
只有bc和awk可以对小数进行运算。
bash[root@server ~]# echo 1+1.2 | bc 2.2 [root@server ~]# awk 'BEGIN {a=1;b=1.2;print a+b}' 2.2
3、如果变量的值是字符串,可以对字符串进行处理显示
bash
[root@localhost ~]# filename=file.tar.gz #定义一个变量
[root@localhost ~]# echo ${#filename} #显示变量里面字符串的长度
11
[root@localhost ~]# echo ${filename:1:3} #切割字符串显示
ile
[root@server ~]# echo ${filename/./-} #替换从左往右的第一个.为-
file-tar.gz
[root@server ~]# echo ${filename//./-} #替换所有的.为-
file-tar-gz
[root@localhost ~]# echo ${filename#*.} #将从字符串第一个字符开始匹配任意字符直到从前往后数第一个.之间的字符删除
tar.gz
[root@localhost ~]# echo ${filename##*.} 将从字符串第一个字符开始匹配任意字符直到从前往后数最后一个.之间的字符删除
gz
[root@localhost ~]# echo ${filename%.*} 将从字符串最后一个字符开始匹配任意字符直到从后往前数第一个.之间的字符删除
file.tar
[root@localhost ~]# echo ${filename%%.*} 将从字符串最后一个字符开始匹配任意字符直到从后往前数最后一个.之间的字符删除
file
4、直接在命令中使用变量
bash
[root@localhost ~]# a=file
[root@localhost ~]# b=file1
[root@localhost ~]# ls -l $a $b
-rw-r--r--. 1 root root 986 Oct 20 21:01 file
-rw-r--r--. 1 root root 17 Oct 20 20:59 file1
2.3 变量的生效范围
1、局部变量(普通变量):只在某个特定范围可以使用的变量,比如只能在当前进程中使用的变量或者是在函数中使用的变量。
- 在当前进程生效的变量,可直接在命令行设置;
- 在脚本文件中生效的变量,可直接在脚本文件中设置;
- 在函数中定义普通变量,作用范围为当前函数:
local var2="value"。
bash
[root@server ~]# cat /shell/test3.sh
#!/bin/bash
f1()
{
echo "$v1"
local v1=200 #函数中定义局部变量,仅在函数中生效
echo "$v1"
}
v1=100 #在脚本中定义的变量,整个脚本都能使用
f1
echo "$v1"
[root@server ~]# bash /shell/test3.sh
100
200
100
2、全局变量(环境变量):可以在创建它们的shell及其派生出来的任意子进程shell中使用。环境变量又可分为自定义环境变量和bash内置的环境变量。
-
bash内置的环境变量
shell内置的环境变量是所有的shell程序都可以使用的变量。shell程序在运行时,都会接收一组变量来确定登录用户名、命令路径、终端类型、登录目录等,这组变量就是环境变量。环境变量会影响到所有的脚本的执行结果。
变量 说明 PATH 命令搜索路径,以冒号为分隔符 HOME 用户主目录的路径名,是cd命令的默认参数 COLUMNS 定义了命令编辑模式下可使用命令行的长度 HISTFILE 命令历史文件 HISTSIZE history 命令输出的记录数 HISTFILESIZE 命令历史文件中包含的最大行数 IFS 定义shell使用的分隔符 LOGNAME 当前的登录名 SHELL shell的全路径名 PWD 当前工作目录 bash[root@server ~]# echo $PATH /root/.local/bin:/root/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin [root@server ~]# echo $SHELL /bin/bash -
自定义环境变量:以下命令可以在当前shell定义环境变量,但是用户退出后该环境变量会丢失,如果需要永久保存必须将自定义环境变量定义在文件中。
-
export var或export var="value" -
declare -x var2="value",可使用+x选项将环境变量变成非环境变量
bash#定义的是局部变量,在bash子进程中无法使用变量name [root@server ~]# name=xiaoming [root@server ~]# bash [root@server ~]# echo $name [root@server ~]# exit #将变量定义为环境变量,在bash子进程可以使用变量name [root@server ~]# export name=xiaoming [root@server ~]# bash [root@server ~]# echo $name xiaoming [root@server ~]# exit -
2.4 案例
1、计算两个变量的和、差、乘积、商和余数
bash
# 方法1
[root@server ~]# vim /shell/chap02/test1.sh
#!/bin/bash
a=$1
b=$2
echo a+b=$(($a+$b))
echo a-b=$((a-b))
echo a*b=$((a*b))
echo a/b=$((a/b))
echo a%b=$((a%b))
[root@server ~]# /shell/chap02/test1.sh 10 3
a+b=13
a-b=7
a*b=30
a/b=3
a%b=1
# 方法2:
[root@server ~]# vim /shell/chap02/test1-1.sh
#!/bin/bash
read -p "please input two number:" a b
echo $a+$b=$(($a+$b))
echo $a-$b=$((a-b))
echo $a*$b=$((a*b))
echo $a/$b=$((a/b))
echo $a%$b=$((a%b))
[root@server ~]# /shell/chap02/test1-1.sh
please input two number:3 4
3+4=7
3-4=-1
3*4=12
3/4=0
3%4=3
2、计算1+......+100的和,计算100以内所有偶数的和,计算100以内所有奇数的和
bash
#100以内的和
[root@server ~]# echo {1..100} | tr " " "+" | bc
5050
[root@server ~]# seq -s "+" 100 | bc
5050
#也可以使用循环实现,此处暂时省略
#奇数的和
[root@server ~]# echo {1..100..2} | tr " " "+" | bc
2500
[root@server ~]# seq -s "+" 1 2 100 | bc
2500
#偶数的和请自行思考
3、批量修改文件名
需求:去掉所有文件的_finish字符信息。
- 准备测试数据:假设在目录/shell/dir1中有如下文件
bash
[root@server ~]# mkdir /shell/dir1
[root@server ~]# touch /shell/dir1/open_{1..5}_finish.jpg
[root@server ~]# touch /shell/dir1/open_{1..5}_finish.png
[root@server ~]# ls /shell/dir1/
open_1_finish.jpg open_2_finish.png open_4_finish.jpg open_5_finish.png
open_1_finish.png open_3_finish.jpg open_4_finish.png
open_2_finish.jpg open_3_finish.png open_5_finish.jpg
- 思考
bash
#1、修改文件名的命令为
mv /shell/dir1/open_1_finish.jpg /shell/dir1/open_1.jpg
#2、可以看到只是去掉文件名中的_finish,可以使用字符串删除方式实现
old_file=/shell/dir1/open_1_finish.jpg
new_file=${old_file/_finish/}
mv $old_file $new_file
#3、需要将/shell/dir1/目录下的文件名依次赋值给old_file变量,需要使用循环实现
[root@server ~]# for file in $(ls /shell/dir1);do old_file=/shell/dir1/$file ;echo $old_file ;done
/shell/dir1/open_1_finish.jpg
/shell/dir1/open_1_finish.png
/shell/dir1/open_2_finish.jpg
/shell/dir1/open_2_finish.png
/shell/dir1/open_3_finish.jpg
/shell/dir1/open_3_finish.png
/shell/dir1/open_4_finish.jpg
/shell/dir1/open_4_finish.png
/shell/dir1/open_5_finish.jpg
/shell/dir1/open_5_finish.png
bash
[root@server ~]# vim /shell/chap02/test2.sh
for file in $(ls /shell/dir1)
do
old_file=/shell/dir1/$file
new_file=${old_file/_finish/}
mv $old_file $new_file
done
[root@server ~]# bash /shell/chap02/test2.sh
[root@server ~]# ls /shell/dir1/
open_1.jpg open_2.jpg open_3.jpg open_4.jpg open_5.jpg
open_1.png open_2.png open_3.png open_4.png open_5.png
4、拓展
| 语法 | var变量未定义 / 为空 | var变量有值非空 | 是否给变量赋值 |
|---|---|---|---|
${var:-word} |
返回 word | 返回 var 原值 | ❌ 不修改 var |
${var:=word} |
返回 word,同时把 var 设为 word | 返回 var 原值 | ✅ 会修改 var |
${var:+word} |
返回空 | 返回 word | ❌ 不修改 var |
${var:?word} |
打印 word 错误信息并脚本退出 | 返回 var 原值 | ❌ 不修改 var |
bash
# :- 如果未定义过port端口,则使用默认的80,port变量不变
[root@server ~]# echo ${port:-80}
80
[root@server ~]# echo $port
#如果定义了,则使用定义的值
[root@server ~]# port=8001
[root@server ~]# echo ${port:-80}
8001
# := 如果未定义日志目录,则自动将log_dir变量设为/var/log/mylog路径
[root@server ~]# echo ${log_dir:=/var/log/mylog}
/var/log/mylog
[root@server ~]# echo $log_dir
/var/log/mylog
# :+ 如果变量为空,什么都不做,否则返回后面字符
[root@server ~]# echo ${info:+test}
[root@server ~]# info="test1"
[root@server ~]# echo ${info:+test}
test
# :? 如果变量为空,则直接打印错误信息
[root@server ~]# echo ${password:?error}
-bash: password: error
[root@server ~]# password=123
[root@server ~]# echo ${password:?error}
123
示例:删除7天前的过期备份数据
bash
#!/bin/bash
find ${path:-/tmp} -name "*.tar.gz" -type f -mtime +7 | xargs rm -f
# find为查找文件命令
# ${path:-/tmp} 表示如果没有定义path则使用/tmp替代
# rm不支持|管道符,使用xargs命令表示将管道符输出传递给rm命令