二、shell中的变量

二、变量

变量是在程序中保存用户数据的一段内存存储空间,变量对应的内存存储空间的内容是可以变化的。可以变化的是变量的值,变量的名字是不变的。

2.1 变量的定义与取消

变量的命名规范:只允许使用数字字母下划线,不能用数字开头。

变量赋值规范:变量名和变量的值之间用=连接,且等号左右不能有空格,如果变量的值含有空格字符,那么需要用引号引起来。

变量的类型:shell是一种动态类型语言和弱类型语言,变量是不分数据类型的,统一都使用字符串存储,但根据变量的上下文环境,允许程序执行一些不同的操作,如:比较、整数加减。

  1. 在命令行中定义:退出当前进程后该变量则失效

    • 方式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
  2. 在文件中定义:永久生效,将命令行中定义变量的语句写入文件中,系统启动后用户登录系统后会自动加载文件。

    • 当前用户可读取的变量,将变量定义在用户家目录下的对应文件中

      shell 复制代码
      ~/.bashrc和~/.bash_profile
    • 所有用户可读取的变量,将变量定义在/etc目录下的对应文件中

      shell 复制代码
      /etc/bashrc,/etc/profile
      /etc/profile.d/*.sh

​ 注意:susu -切换用户时加载的文件不一样,所以具体在哪个文件中定义变量,请根据实际情况选择。

  1. 位置参数变量

    许多情况下,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
  2. 取消变量

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
  • 方式三:查看一些变量的值

    bash 复制代码
    set:查看所有变量
    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 varexport 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命令
相关推荐
杨云龙UP1 小时前
ODA/Oracle 19c CDB/PDB 环境下报错ORA-65162:common user密码过期问题排查与处理_2026-05-15
linux·运维·数据库·oracle·dba·db
wjykp1 小时前
5.cypher语句组合与复杂操作
linux·前端·javascript
其实防守也摸鱼1 小时前
[特殊字符] Docker + LMArena2API 部署全流程:从环境准备到接口调用,一步到位
运维·网络·安全·web安全·docker·容器·大模型
志栋智能1 小时前
超自动化巡检:如何应对海量增长的基础设施?
运维·自动化
苏宸啊1 小时前
磁盘结构、存储原理
linux
代码讲故事1 小时前
Redis生产环境批量的key需要删除,如何优雅实现且不影响线上环境?有密码认证的如何实现批量删除?有哪些实现方法?
运维·redis·缓存·高并发·删除·批量·生产环境
Irene19912 小时前
两种 Linux 发行版:Ubuntu 与 CentOS Shell 环境核心差异对比(查看 Linux 版本,Hadoop 是什么)
linux·ubuntu·centos
j7~2 小时前
【Linux系统】基础IO(文件描述)(1)
linux·服务器·c++·文件·基础io
MXsoft6182 小时前
**配置文件频繁变更导致故障?版本控制****+****合规检查必不可少**
运维