二、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.mzph.cn/diannao/81552.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java_Lambda表达式

1、背景 lambda表达式是Java SE 8中一个重要的新特性。lambda表达式允许你通过表达式来代替功能接口。lambda表达式就和方法一样样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式和一个代码块)。La…

给QCustomPlot添加一个QScrollBar滚动条、限制缩放范围、自动设置大小和右边栏垂直缩放

实现效果 实现思路 从QCustomPlot类派生一个类,进行个性化设置,在轴矩形的上边设置Margin,放一个滚动条,设置滚动条的样式 常量定义 #define NQSCRB 1000构造函数初始化 // 设置QScrollBar的样式// 顶部空--5,左侧空--6

实验-组合电路设计1-全加器和加法器(数字逻辑)

目录 一、实验内容 二、实验步骤 2.1 全加器的设计 2.2 加法器的设计 三、调试过程 3.1 全加器调试过程 2.加法器的调试过程 四、实验使用环境 五、实验小结和思考 一、实验内容 a) 介绍 在这次实验中,你将熟悉 Logisim 的操作流程,并且学习…

Linux进程控制与替换详解

进程创建 fork函数初识 在linux中fork函数是非常重要的函数,它从已存在进程中创建⼀个新进程。新进程为子进程,而原进程为父进程。 进程调用fork,当控制转移到内核中的fork代码后,内核做: • 分配新的内存块和内核数据结构给子进程 • 将父进程部分数据结构内容拷贝至…

Vue3学习笔记2——路由守卫

路由守卫 全局 router.beforeEach((to, from, next) > {})router.afterEach((to, from, next) > {}) 组件内守卫 beforeRouteEnter((to, from, next) > {})beforeRouteUpdate((to, from, next) > {})beforeRouteLeave((to, from, next) > {}) 路由独享 be…

AI与无人零售:如何通过智能化技术提升消费者体验和运营效率?

引言:无人零售不只是无人值守 你走进一家无人便利店,没有迎宾、没有收银员,甚至没有一个人在场,但你刚拿起商品,货架旁的摄像头就悄悄“看懂”了你的动作,系统已经在后台为你记账。你以为只是没人管&#x…

如何在3dMax中使用UVW展开修改器?

UVW展开(Unwrap UVW)修改器是3dmax中的一个强大工具,允许对纹理如何应用于3D模型进行精确控制。 与更简单的UVW Map修改器不同,Unwrap UVW修改器提供了高级选项,用于手动编辑纹理映射,对于详细和复杂的模型来说是必不可少的。 在本文中,我们将探讨增强您对Unwrap UVW修…

【Linux】进程优先级与进程切换理解

🌟🌟作者主页:ephemerals__ 🌟🌟所属专栏:Linux 目录 前言 一、进程优先级 1. 什么是进程优先级 2. 为什么有进程优先级 3. 进程优先级的作用 4. Linux进程优先级的本质 5. 修改进程优先级 二、进…

【Hive入门】Hive高级特性:事务表与ACID特性详解

目录 1 Hive事务概述 2 ACID特性详解 3 Hive事务表的配置与启用 3.1 启用Hive事务支持 3.2 创建事务表 4 Hive事务操作流程 5 并发控制与隔离级别 5.1 Hive的锁机制 5.2 隔离级别 6 Hive事务的限制与优化 6.1 主要限制 6.2 性能优化建议 7 事务表操作示例 7.1 基本…

二叉树算法精解(Java 实现):从遍历到高阶应用

引言 二叉树(Binary Tree)作为算法领域的核心数据结构,在搜索、排序、数据库索引、编译器语法树构建等众多场景中都有着广泛应用。无论是初学者夯实算法基础,还是求职者备战技术面试,掌握二叉树相关算法都是不可或缺的…

ES6入门---第二单元 模块二:关于数组新增

一、扩展运算符。。。 1、可以把ul li转变为数组 <script>window.onloadfunction (){let aLi document.querySelectorAll(ul li);let arrLi [...aLi];arrLi.pop();arrLi.push(asfasdf);console.log(arrLi);};</script> </head> <body><ul><…

Nature正刊:新型折纸启发手性超材料,实现多模式独立驱动,变形超50%!

机械超材料是一种结构化的宏观结构&#xff0c;其几何排列方式具有独特的几何结构&#xff0c;从而具有独特的力学性能和变形模式。超材料的宏观特性取决于中观尺度晶胞的具体形状、尺寸和几何取向。经典的结构化晶胞&#xff0c;例如以拉伸为主的八面体桁架单元和以弯曲为主的…

Servlet(二)

软件架构 1. C/S 客户端/服务器端 2. B/S 浏览器/服务器端&#xff1a; 客户端零维护&#xff0c;开发快 资源分类 1. 静态资源 所有用户看到相同的部分&#xff0c;如&#xff1a;html,css,js 2. 动态资源 用户访问相同资源后得到的结果可能不一致&#xff0c;如&#xff1a;s…

循环缓冲区

# 循环缓冲区 说明 所谓消费&#xff0c;就是数据读取并删除。 循环缓冲区这个数据结构与生产者-消费者问题高度适配。 生产者-产生数据&#xff0c;消费者-处理数据&#xff0c;二者速度不一致&#xff0c;因此需要循环缓冲区。 显然&#xff0c;产生的数据要追加到循环缓…

嵌入式硬件篇---STM32 系列单片机型号命名规则

文章目录 前言一、STM32 型号命名规则二、具体型号解析1. STM32F103C8T6F103:C:8:T6:典型应用2. STM32F103RCT6F103:R:C:T6:典型应用三、命名规则扩展1. 引脚数与封装代码2. Flash 容量代码3. 温度范围代码四、快速识别技巧性能定位:F1/F4后缀差异硬件设计参考:引脚数…

MySQL 中日期相减的完整指南

MySQL 中日期相减的完整指南 在 MySQL 中&#xff0c;日期相减有几种不同的方法&#xff0c;具体取决于你想要得到的结果类型&#xff08;天数差、时间差等&#xff09;。 1. 使用 DATEDIFF() 函数&#xff08;返回天数差&#xff09; SELECT DATEDIFF(2023-05-15, 2023-05-…

传奇各版本迭代时间及内容变化,屠龙/嗜魂法杖/逍遥扇第一次出现的时间和版本

​【早期经典版本】 1.10 三英雄传说&#xff1a;2001 年 9 月 28 日热血传奇正式开启公测&#xff0c;这是传奇的第一个版本。游戏中白天与黑夜和现实同步&#xff0c;升级慢&#xff0c;怪物爆率低&#xff0c;玩家需要靠捡垃圾卖金币维持游戏开销&#xff0c;遇到高级别法师…

重塑数学边界:人工智能如何引领数学研究的新纪元

目录 一、人工智能如何重新定义数学研究的边界 &#xff08;一&#xff09;数学与AI的关系&#xff1a;从基础理论到创新思维的回馈 &#xff08;二&#xff09;AI的创造力&#xff1a;突破传统推理的局限 &#xff08;三&#xff09;AI对数学研究的潜在贡献&#xff1a;创…

IP伪装、代理池与分布式爬虫

一、动态代理IP应用&#xff1a;代理池的获取、选择与使用 代理池技术的核心是通过动态切换IP地址&#xff0c;让爬虫看起来像不同用户在访问网站&#xff0c;从而规避封禁。 &#xff08;一&#xff09;代理池的获取途径 1. 免费代理&#xff1a;低成本但高风险 免费代理可…

自然语言处理实战:用CRF打造高精度命名实体识别系统

## 一、从标签游戏到智能系统:命名实体识别的前世今生 在信息爆炸的互联网时代,我们每天面对的海量文本中隐藏着无数有价值的信息。想象一下,当你在浏览新闻时,系统能自动标红所有人名、地点和机构名称——这就是命名实体识别(NER)技术的魔力。从早期的规则匹配到如今的…