shell 变量和引用

目录

一. 变量和引用

1.1 什么是变量

1.2 变量的名称

1.3 变量的类型

1.4 变量的定义

1.5 自定义变量

1.6 环境变量

1.7 位置变量

二、变量赋值和作用域

1.1 显示赋值 :变量名=变量值

1.2 read 从键盘读入变量值

1.3 变量和引号

1.4 变量的作用域

1.5变量的运算


一. 变量和引用

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就认为做加法的变量为整数,否则就不是整数。
bclinux下的一个计算器程序(适合整数及小数运算)[root@node13 test9]# echo `seq -s "+" 10` = `seq -s "+" 10 | bc ` seq是生成数字序列,-s是指定数字序列之间的分隔符
$[]用于整数运算
awkawk既可以用于整数运算,也可以用于小数运算
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命令表示管道符进行数据转换