当前位置: 首页 > ds >正文

二、shell脚本--变量与数据类型

1. 变量的定义与使用

定义变量:简单直接

在 Shell 里定义变量相当容易

  • 基本格式: variable_name=value
  • 关键点 ❗:赋值号 = 的两边绝对不能有空格!这绝对是初学者最容易踩的坑之一 😨,务必留意!
  • 起名字的规矩: 变量名通常由字母、数字、下划线构成,而且不能用数字开头。大家习惯用全大写表示环境变量或脚本常量,用小写或驼峰式表示本地变量
  • 值的“类型”: Shell 对变量不太讲究类型,基本上都当字符串来看待。就算你存个数字,它本质上也是个字符串(不过在做数学计算时,Shell 会尝试把它当数字处理)。
  • 给值加引号的学问:
    • 如果值里没有空格或特殊符号,可以省略引号myvar=hello
    • 如果值里有空格必须单引号 ' '双引号 " " 包起来:mymessage='Hello World'mymessage="Hello World"
    • 单引号 (’ ')“死心眼”引用。里面写啥就是啥,完全按字面处理变量 ($var) 不会展开,特殊字符也失去魔力。
    • 双引号 (" ")“灵活”引用。里面的变量引用 ($var) 会被替换成值,某些特殊字符(像 $\、```````)仍然有效
# 正确的变量定义
name="Alice"
age=30
city="Beijing"
greeting1='你好, ${name}!' # 单引号内 ${name} 不会被替换
greeting2="你好, ${name}!" # 双引号内 ${name} 会被替换成 Alice# 错误的变量定义 (等号两边有空格)
# wrong_var = "error"
引用变量:取出仓库里的东西

拿到用上变量存的值,就在变量名前面加个 $ 符号:

  • 基础用法: $variable_name
  • 更稳妥的用法: ${variable_name} (用花括号 {} 包起来)

为什么推荐用 ${} 🤔

  • 划清界限: 当变量名后面紧跟着其他字符时,花括号能明确告诉 Shell 变量名到哪里结束,避免混淆。比如 ${user}_id 就很清晰。
  • 高级玩法: 之后要玩转字符串操作(比如截取、替换),花括号是必备的。
#!/bin/bashuser="Bob"
action="studying"
echo "用户是: ${user}" # 输出 用户是: Bob
echo "${user} 正在 ${action} Shell。" # 输出 Bob 正在 studying Shell。
只读变量与删除变量
  • 只读变量 (Read-only): 把它锁起来 🛡️
    • readonly 命令,能让一个变量变成只读
    • 一旦设为只读,它的值就不能再改,也不能用 unset 删除
    • 适合定义脚本里不希望被意外修改的常量值。
#!/bin/bashreadonly app_version="1.0.2"
echo "当前应用版本: ${app_version}"# 尝试修改会报错
# app_version="1.0.3" # Error!# 尝试删除也会报错
# unset app_version # Error!
  • 删除变量 (Unset): 清空仓库 🗑️
    • unset 命令可以彻底删除一个变量(名字和值都没了)。
    • 记住: readonly 的变量是删不掉的。
#!/bin/bashtmp_dir="/tmp/my_app_temp"
echo "临时目录: ${tmp_dir}"unset tmp_dir # 删除这个变量# 再用它,就是空的了
echo "删除后的临时目录: ${tmp_dir}" # 这里会输出空行

2. 变量的作用域与类型

搞清楚变量在什么地方能用(作用域)非常关键。Shell 里主要有两大类作用域的变量:

本地变量 (普通变量) 🏠
  • 如何产生: 默认就是它!只要你用 variable_name=value 这样直接赋值,得到的就是本地变量。
  • 活动范围: 非常有限,只在创建它的那个当前的 Shell 进程里有效。可以想象成你房间里的私人物品,别人进不来拿不到。
  • 传给下一代? 不行 ❌。它不会被当前 Shell 启动的子进程(比如你运行的另一个脚本或命令)自动认识。子进程对父进程的本地变量一无所知
#!/bin/bash
# 定义本地变量
my_secret="这是我的小秘密"
echo "父脚本说,我有秘密: ${my_secret}"# 启动一个子 Shell 试试看
echo "--- 启动子 Shell ---"
bash -c 'echo "子 Shell 说,秘密是啥? ${my_secret}"' # 这里 ${my_secret} 是空的,子 Shell 拿不到
echo "--- 子 Shell 结束 ---"
环境变量 (全局变量) 🌍 与 export 命令:让信息传递下去
  • 活动范围: 环境变量的影响力更大,不只在当前 Shell 里有效。

  • 传给下一代? 可以 ✅!它们能被当前 Shell 启动的所有子进程继承。就像家族的公开信息,子子孙孙都能知道和使用。

  • 魔法棒 export ✨: 想让一个变量从本地的“私人物品”变成能被子进程继承“公开信息”(环境变量)吗?那就得用 export 命令!export 的核心作用就是把一个变量“发布”到环境中去。

  • export两种常见姿势 ✌️:

    1.先有鸡(本地变量),再有蛋(导出):

    # 1. 先定义一个普普通通的本地变量
    my_local_var="初始值"
    # 2. 然后,用 export 把它“提拔”成环境变量
    export my_local_var
    

    2.一步到位,定义的同时就导出: (这种更常用简洁)

    export shared_config_path="/etc/my_app/config"
    
  • 效果立竿见影: 变量一旦被 export,就光荣地成为了环境变量。从此刻起,这个 Shell 再启动任何新的子进程,这些子进程都能看到并使用这个环境变量了。

  • 实例见真章:

#!/bin/bash
# 本地变量,无法继承
local_info="只在父脚本可见"
# 用 export 创建环境变量,可以继承
export shared_info="父子都能看到"echo "父脚本说,本地信息: ${local_info}"
echo "父脚本说,共享信息: ${shared_info}"# 启动子 Shell 来验证
echo "--- 启动子 Shell ---"
bash -c 'echo "子 Shell 说,本地信息: ${local_info}"; echo "子 Shell 说,共享信息: ${shared_info}"'
# 注意看输出:子 Shell 里的 local_info 是空的,但 shared_info 有值!
echo "--- 子 Shell 结束 ---"
  • 查看当前的环境变量 📋: 想知道当前环境里都有哪些“公开信息”?在终端敲 env 或者 printenv 命令就行。你会看到很多系统预设的环境变量(比如 PATH, HOME, USER 等),以及你自己用 export 添加的那些。
让环境变量“永久生效”:修改配置文件 ⚙️

上面用 export 设置的环境变量只是临时的,一旦你关闭当前的 Shell 窗口(终端),它们就消失了 💨。如果希望某个环境变量长期有效,每次打开新终端都能用,就需要把它写进 Shell 的配置文件里。

  • 常见的配置文件 (Bash 为例):

    • ~/.bashrc: 最常用 👍。每次打开新的交互式 Shell(比如新开一个终端窗口)时,这里面的命令会被执行。非常适合放个人常用的环境变量和别名。
    • ~/.bash_profile (或 ~/.profile): 当你登录系统时(比如 TTY 登录,或者 SSH 登录)会执行一次。它通常会调用 ~/.bashrc。如果主要在图形界面下开终端,~/.bashrc 更常用。
    • /etc/profile: 系统全局的配置文件,影响所有用户的登录 Shell。修改它需要管理员权限。
  • 如何配置:

    1. 选择一个合适的配置文件(初学者推荐 ~/.bashrc)。

    2. 用文本编辑器打开它 (e.g., nano ~/.bashrcvim ~/.bashrc)。

    3. 在文件末尾添加你的 export 命令,例如:

      export MY_API_KEY="your_secret_api_key_here"
      export JAVA_HOME="/usr/lib/jvm/java-11-openjdk-amd64"
      
    4. 保存文件并退出编辑器。

    5. 让更改生效:

      • 方法一 (推荐):当前终端执行 source ~/.bashrc 命令,立即加载配置。
      • 方法二: 关闭当前终端,重新打开一个新的终端窗口,新窗口会自动加载 .bashrc
  • 示例:添加一个永久环境变量

    # 把 export 命令追加到 .bashrc 文件末尾
    echo 'export MY_APP_DATA_DIR="/var/myapp/data"' >> ~/.bashrc# 让当前终端也立刻知道这个新变量
    source ~/.bashrc# 现在可以验证一下了
    echo "我的应用数据目录是: ${MY_APP_DATA_DIR}"
    

    现在,每次你打开新的终端,${MY_APP_DATA_DIR} 这个变量就自动可用了!🎉

本地变量 vs 环境变量:总结回顾 🆚
特性本地变量 (普通变量) 🏠环境变量 (全局变量) 🌍
定义方式variable_name=value (默认)使用 export 命令 (如 export var=val)
作用域仅限当前 Shell 进程当前 Shell 进程 其所有子进程
继承性不被 子进程继承 ❌可被 子进程继承 ✅
持久性临时,随 Shell 关闭消失临时(若仅 export),可配置为永久(修改配置文件)
关键命令(默认)export (设置/导出), env/printenv (查看)
用途脚本内部计算、临时存储PATH设置、跨脚本共享配置、传递给子命令

一句话总结: 默认变量是本地的,用 export 把它变成环境变量,才能让子进程看到。想永久生效?写进配置文件 (~/.bashrc)。

变量描述
$0当前脚本的名称
$1$9脚本传递给脚本的第一个到第九个参数
$#脚本传递给脚本的参数个数
$@脚本传递给脚本的所有参数(如果加引号:“$@”,每个参数保持独立)
$*脚本传递给脚本的所有参数(如果加引号:“$*”,参数作为一个整体)
$$当前脚本的进程ID
$!最近一次后台运行命令的进程ID
$?上一个命令的退出状态码
$_上一个命令的最后一个参数
$-当前Shell的选项
$IFS输入字段分隔符,默认是空格、制表符和换行符赞
位置参数变量 🔢

(同前) 脚本运行时,跟在后面的参数。用 $0, $1, $2${10}… 引用。

#!/bin/bash
# 文件名: show_params.sh
echo "脚本名字 (\$0): $0"
echo "第一个参数 (\$1): $1"
echo "第二个参数 (\$2): $2"
# 执行方式: ./show_params.sh apple banana
特殊变量 ✨

(同前) Shell 自带的,有特殊含义。$# (个数), $* (整体参数), $@ (独立参数), $? (退出码), $$ (当前PID), $! (后台PID)。

#!/bin/bash
# 文件名: special_vars.sh
echo "收到参数个数 (\$#): $#"
ls /no_such_file_or_dir # 制造一个错误
echo "上个命令是否成功 (\$?): $?" # 0 代表成功, 非 0 代表失败
echo "本脚本的进程号 (\$\$): $$"
echo "--- 逐个看参数 ---"
idx=1
for param in "$@" # 推荐用 "$@" 来循环处理所有参数
do
echo "第 ${idx} 个参数是: ${param}"
idx=$((idx + 1))
done
# 执行方式: ./special_vars.sh "带空格的参数" 第二个参数

3. 字符串操作 ✂️🔗📏

(这部分内容和之前的版本相同,变量名英文,提示中文,无代码缩进)

字符串的定义与拼接

(同前)

#!/bin/bash
prefix="file_"
timestamp=$(date +%Y%m%d)
full_filename="${prefix}${timestamp}.log"
echo "完整文件名: ${full_filename}"
获取字符串长度 📏

(同前) ${#variable_name}

#!/bin/bash
sentence="学习 Shell 很有趣!"
len=${#sentence}
echo "这句话是: '${sentence}'"
echo "它的长度是: ${len}"
提取子字符串 ✂️

(同前) ${variable_name:offset:length}

#!/bin/bash
full_url="https://example.com/products/item123"
protocol=${full_url:0:5} # 从第0位开始,取5个字符
echo "协议是: ${protocol}" # https
product_id=${full_url:26} # 从第26位开始,取到末尾
echo "产品ID是: ${product_id}" # item123
字符串替换 🔁

(同前) ${var/pat/rep} (首个), ${var//pat/rep} (全部)

#!/bin/bash
raw_text="path is /home/user/data dir"
fixed_text=${raw_text/path is/directory is} # 替换第一个
echo "修正后的文本: ${fixed_text}"
no_spaces=${raw_text// /_} # 替换所有空格为下划线
echo "无空格版本: ${no_spaces}"

变量与数据类型练习题 🧠✍️

题目一:变量定义
❓ 以下哪个 Shell 变量定义是错误的,为什么?
A. my_var=hello
B. _value="some text"
C. count = 10
D. message='Error code: $?'

题目二:变量引用
❓ 假设有变量 filename="data.csv",如何安全地将其与字符串 _backup 拼接成 data.csv_backup?写出推荐的写法。

题目三:变量作用域辨析
本地变量(普通变量)和环境变量(全局变量)在被子进程继承方面有什么关键区别?哪个命令用于将本地变量变为环境变量?

题目四:位置参数
❓ 一个脚本 run.sh 被这样调用:./run.sh first second third。在 run.sh 内部,变量 $3 的值是什么?

题目五:特殊变量含义
❓ 执行一个命令后,应该检查哪个特殊变量来判断该命令是否成功执行?如果成功,这个变量的值通常是什么?

题目六:$* vs $@ 关键场景
❓ 当脚本的参数是 "文件 1.txt""文件 2.txt" 时,for i in "$*"for i in "$@" 遍历的结果有何不同?哪个更适合逐个处理这些文件名?

题目七:字符串长度
❓ 如何获取变量 address="北京市海淀区"长度

题目八:子串提取
❓ 给定 version_str="app-1.0.5-release",如何提取出中间的版本数字 1.0.5

题目九:字符串替换
❓ 如何将字符串 path_var="/usr/bin:/usr/local/bin:/bin"所有的冒号 : 替换为空格


参考答案 ✅💡

答案一:
C. count = 10 是错误的 ❌。
原因: Shell 变量赋值时,等号 = 两边绝对不能有空格。正确写法应为 count=10

答案二:
推荐使用花括号 {} 来界定变量名:${filename}_backup ✅。

答案三:
关键区别:本地变量 不会被子进程继承,而环境变量 可以被子进程继承 ✅。使用 export 命令将本地变量变为环境变量。

答案四:
变量 $3 的值是 third

答案五:
应该检查 $? ❓。如果命令成功执行,$? 的值通常是 0 ✅。非零值表示有错误发生。

答案六:
结果不同:

  • for i in "$*": 循环只执行一次,变量 i 的值是整个字符串 "文件 1.txt 文件 2.txt"
  • for i in "$@": 循环会执行两次。第一次 i"文件 1.txt",第二次 i"文件 2.txt"

因此,for i in "$@" 更适合逐个、且保持参数完整性地处理这些文件名 👍。

答案七:
使用 ${#address} 可以获取其长度 📏。

答案八:
可以使用 ${version_str:4:5}

  • 4 是起始偏移量 (索引从0开始,所以第5个字符’1’的索引是4)。
  • 5 是要提取的长度 (‘1.0.5’ 这5个字符)。

答案九:
使用双斜杠 // 进行全局替换:${path_var//:/ } 🔁。(冒号被替换为空格)。

http://www.xdnf.cn/news/3843.html

相关文章:

  • Python datetime库的用法 Python从入门到入土系列第3篇-洞察标准库DateTime
  • 【Spring】Spring中8种常见依赖注入使用示例
  • 健康养生新主张
  • web应用开发说明文档
  • matlab学习之旅
  • 数据结构---
  • 实战项目:基于控制台与数据库的图书管理系统开发指南
  • C语言中memmove和memcpy
  • 智慧校园整体解决方案-5PPT(65页)
  • python中的异常处理
  • 【CF】Day50——Codeforces Round 960 (Div. 2) BCD
  • 数学实验Matlab
  • 多把锁以及线程死锁问题
  • Linux-GRUB全面指南
  • CUDA输出“hello world”
  • 多数据源动态切换
  • 算法每日一题 | 入门-顺序结构-数字反转
  • (38)VTK C++开发示例 ---纹理裁剪
  • C++负载均衡远程调用学习之异步消息任务功能与连接属性
  • CVPR2021 | 重新思考视觉Transformer中的自注意力机制
  • Java学习手册:Spring 生态其他组件介绍
  • 单细胞测序试验设计赏析(一)
  • AWS在跨境电商中的全场景实践与未来生态构建
  • D. 例题3.2.2 整数划分问题
  • 二种MVCC对比分析
  • 学习黑客风险Risk
  • iOS启动优化:从原理到实践
  • 2025年渗透测试面试题总结-拷打题库35(题目+回答)
  • 【C++】:C++17新特性
  • Vivado FPGA 开发 | 创建工程 / 仿真 / 烧录