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

八、Linux Shell 脚本:变量与字符串

作者:IvanCodes
日期:2025年8月3日
专栏:Linux教程

在Shell脚本编程中变量存储和操作数据基石。理解如何定义、使用、传递变量以及如何处理字符串,是编写高效、灵活脚本的第一步,也是最关键的一步。

思维导图

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一、变量的定义与使用

1.1 定义变量

  • 基本格式: variable_name=value
  • 关键点:赋值号 = 的两边绝对不能有空格!这是初学者最常犯的错误之一,务必留意!
  • 命名规则: 变量名通常由字母、数字、下划线构成,且不能以数字开头。习惯上,全大写用于环境变量常量小写或驼峰式用于本地变量
  • 值的“类型”: Shell 不严格区分数据类型,几乎所有值都可视为字符串。即使是数字,在进行算术运算前,本质上也是字符串。

引号的使用规则:

如果值不包含空格或特殊字符,可以省略引号myvar=hello
如果值包含空格必须使用单引号 ' '双引号 " " 包围:message='Hello World'
单引号 (’ ')强引用“字面”引用。其内部所有内容都按原样处理,变量不会被解析,特殊字符也失去其特殊含义。
双引号 (" ")弱引用“解析”引用。其内部的变量引用 ($var) 会被替换为变量的值,某些特殊字符 (如 $\`) 依然有效

# 正确的变量定义
name="Alice"
age=30
city="Beijing"
greeting1='你好, ${name}!' # 单引号内 ${name} 不会被替换
greeting2="你好, ${name}!" # 双引号内 ${name} 会被替换成 Alice# 错误的变量定义 (等号两边有空格)
# wrong_var = "error"

1.2 引用变量

获取或使用变量的值,在变量名前加上 $ 符号。

  • 基础用法: $variable_name
  • 推荐用法: ${variable_name} (使用花括号 {} 包围)

为什么推荐使用 ${}

明确边界: 当变量名后紧跟其他字符时,花括号能清晰地界定变量名的范围,避免歧义。例如,${user}_id 可以正确解析,而 $user_id 会试图查找一个名为 user_id 的变量。
高级功能: 所有高级字符串操作 (如截取、替换) 都必须在花括号内进行。

#!/bin/bash
user="Bob"
action="studying"
echo "用户是: ${user}"
echo "${user}正在${action}Shell。"

1.3 只读变量与删除变量

  • 只读变量 (readonly):
    使用 readonly 命令可以将一个变量锁定,使其变为只读。一旦设置,其值不能被修改,也无法被 unset 删除。非常适合定义脚本中的常量
#!/bin/bash
readonly APP_VERSION="1.0.2"
echo "当前应用版本: ${APP_VERSION}"# 尝试修改将导致错误
# APP_VERSION="1.0.3"# 尝试删除也将导致错误
# unset APP_VERSION
  • 删除变量 (unset):
    使用 unset 命令可以彻底删除一个变量 (包括其名称和值)。注意: readonly 的变量无法被删除。
#!/bin/bash
tmp_dir="/tmp/my_app_temp"
echo "临时目录: ${tmp_dir}"unset tmp_direcho "删除后的临时目录: ${tmp_dir}" # 此处将输出空行

二、变量的作用域

变量的有效范围 (作用域) 是脚本编程中的核心概念

2.1 本地变量

  • 定义方式: 默认通过 variable_name=value 定义的都是本地变量
  • 作用范围: 仅在创建它的当前 Shell 进程中有效。
  • 继承性: 无法被当前 Shell 启动的子进程 (如执行另一个脚本) 自动继承
#!/bin/bash
# 定义本地变量
my_secret="这是我的小秘密"
echo "父脚本中的秘密: ${my_secret}"# 启动一个子Shell
bash -c 'echo "子Shell中能看到秘密吗? ${my_secret}"' # 此处 ${my_secret} 为空

2.2 环境变量export 命令

  • 作用范围:当前 Shell 及其启动的所有子进程中都有效
  • 继承性: 可以被子进程继承
  • export 命令: 该命令用于将一个本地变量 “发布” 或 “导出” 为环境变量,使其能够被子进程访问。

export 的两种用法:

  1. 先定义,后导出:
my_local_var="初始值"
export my_local_var
  1. 定义并同时导出 (更常用):
export SHARED_CONFIG_PATH="/etc/my_app/config"
  • 验证继承性:
#!/bin/bash
local_info="只在父脚本可见"
export shared_info="父子都能看到"echo "父脚本中的本地信息: ${local_info}"
echo "父脚本中的共享信息: ${shared_info}"# 启动子Shell来验证
bash -c 'echo "子Shell中的本地信息: ${local_info}"; echo "子Shell中的共享信息: ${shared_info}"'
# 输出显示:子Shell中local_info为空,但shared_info有值!
  • 查看环境变量: 使用 envprintenv 命令可以列出当前所有的环境变量。

2.3 让环境变量“永久生效”

通过 export 设置的环境变量是临时的,随当前 Shell 会话关闭而失效。要使其永久生效,需要将其写入Shell的配置文件

常用配置文件 (Bash):

~/.bashrc: 常用。每次打开新的交互式终端时加载。适合个人常用的环境变量。
~/.bash_profile: 用户登录时加载一次。
/etc/profile: 最常用。系统全局配置,影响所有用户

配置步骤:
1. 用文本编辑器打开配置文件 (如 vim /etc/profile)
2. 在文件末尾添加 export 命令:

export JAVA_HOME="/usr/lib/jvm/java-11-openjdk-amd64"
export PATH=$PATH:$JAVA_HOME/bin

3.保存并退出
4. 使配置立即生效,执行 source /etc/profile重新打开一个终端

2.4 作用域总结

特性本地变量环境变量
定义方式variable_name=value (默认)使用 export 命令 (如 export var=val)
作用域仅限当前 Shell 进程当前 Shell 进程 其所有子进程
继承性不被 子进程继承可被 子进程继承
持久性临时,随 Shell 关闭消失临时(若仅 export),可配置为永久(修改配置文件)
关键命令(无)export, env, printenv
用途脚本内部临时存储、计数器PATH设置、跨脚本共享配置、系统级参数

三、预定义与特殊变量

Shell 预先定义了一些特殊变量,它们在特定上下文中有固定含义

变量描述
$0当前脚本的名称
$1 - $9第一个到第九个位置参数
$#传递给脚本的参数总个数
$@所有位置参数列表,加引号 "$@" 后每个参数为独立字符串
$*所有位置参数列表,加引号 "$*" 后所有参数合并为单个字符串
$$当前脚本的进程ID (PID)
$!最近一个放入后台的作业的进程ID (PID)
$?上一个命令的退出状态码 (0表示成功,非0表示失败)
$_上一个命令的最后一个参数
$IFS内部字段分隔符 (默认为空格、制表符、换行符)
  • 位置参数变量:
#!/bin/bash
# 文件名: show_params.sh
echo "脚本名 (\$0): $0"
echo "第一个参数 (\$1): $1"
echo "第二个参数 (\$2): $2"
# 执行: ./show_params.sh apple banana
  • 特殊含义变量:
#!/bin/bash
# 文件名: special_vars.sh
echo "收到参数个数 (\$#): $#"
ls /no_such_dir
echo "上个命令的退出状态码 (\$?): $?"
echo "本脚本的进程号 (\$\$): $$"

四、字符串操作

Shell 提供了内置的、强大的字符串处理能力

4.1 获取字符串长度

使用 ${#variable_name} 语法。

#!/bin/bash
sentence="学习 Shell 很有趣!"
len=${#sentence}
echo "字符串 '${sentence}' 的长度是: ${len}"

4.2 提取子字符串

使用 ${variable_name:offset:length} 语法。

#!/bin/bash
full_url="https://example.com/products/item123"
protocol=${full_url:0:5}
echo "协议是: ${protocol}"
product_id=${full_url:26}
echo "产品ID是: ${product_id}"

4.3 字符串替换

  • ${variable/pattern/replacement}: 只替换第一个匹配。
  • ${variable//pattern/replacement}: 替换所有匹配。
#!/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}"

4.4 字符串删除 (裁剪)

  • ${variable#pattern}: 从开头删除最短匹配。
  • ${variable##pattern}: 从开头删除最长匹配。
  • ${variable%pattern}: 从结尾删除最短匹配。
  • ${variable%%pattern}: 从结尾删除最长匹配。
    pattern 可以使用 * 通配符。
#!/bin/bash
filepath="/var/log/httpd/access.log"
# 从开头删除最短的 */
filename=${filepath#*/} # var/log/httpd/access.log
# 从开头删除最长的 */
basename=${filepath##*/} # access.log
# 从结尾删除最短的 .*
name_no_ext=${basename%.*} # access
# 从结尾删除最长的 /
dirname=${filepath%/*} # /var/log/httpd
echo "文件名: ${basename}"
echo "目录名: ${dirname}"
echo "无后缀名: ${name_no_ext}"

练习题

题目:

  1. 以下哪个 Shell 变量定义是错误的,为什么?
    A. my_var=hello
    B. _value="some text"
    C. count = 10
    D. message='Error code: $?'
  2. 假设有变量 filename="data.csv",如何安全地将其与字符串 _backup 拼接成 data.csv_backup
  3. 本地变量环境变量被子进程继承方面有什么关键区别?哪个命令用于将本地变量变为环境变量?
  4. 一个脚本 run.sh 被这样调用:./run.sh first "second arg" third。在 run.sh 内部,变量 $# 的值是什么?
  5. 执行一个不存在的命令 non_exist_cmd 后,$? 的值通常是什么 (一个非零值)?
  6. 当脚本的参数是 "文件 1.txt""文件 2.txt" 时,for i in "$*"for i in "$@" 遍历的结果有何不同
  7. 如何获取变量 address="北京市海淀区"长度
  8. 给定 version_str="app-1.0.5-release",如何提取出中间的版本数字 1.0.5
  9. 如何将字符串 path_var="/usr/bin:/usr/local/bin:/bin"所有的冒号 : 替换为空格
  10. readonly my_const="cannot_change" 执行后,再执行 unset my_const 会发生什么?
  11. my_var="value"unset my_var 之后,echo ${my_var} 的输出有何不同?
  12. 如何将一个名为 API_TOKEN 的本地变量传递给一个用 python my_script.py 启动的子进程?
  13. echo $$ 命令会输出什么?
  14. 给定 full_path="/home/user/documents/report.docx",如何只提取出文件名 report.docx
  15. 给定 full_path="/home/user/documents/report.docx",如何只提取出目录路径 /home/user/documents

答案与解析:

  1. 答案: C. count = 10 是错误的。
    解析: Shell 变量赋值时,等号 = 两边绝对不能有空格。正确写法应为 count=10
  2. 答案: ${filename}_backup
    解析: 推荐使用花括号 {} 来明确界定变量名,防止 Shell 将 _backup 视为变量名的一部分。
  3. 答案: 本地变量 不会被子进程继承,而环境变量 可以被子进程继承。export 命令用于将本地变量变为环境变量。
  4. 答案: $# 的值是 3
    解析: "second arg" 因为被双引号包围,被视为一个独立的、完整的参数
  5. 答案: $? 的值会是一个非零值 (通常是127,表示 “command not found”)。
    解析: $? 记录上一个命令的退出状态码0 代表成功,任何非零值都表示某种形式的失败。
  6. 答案:
    for i in "$*": 循环只执行一次,变量 i 的值是整个字符串 "文件 1.txt 文件 2.txt"
    for i in "$@": 循环会执行两次。第一次 i"文件 1.txt",第二次 i"文件 2.txt""$@" 更适合逐个处理带空格的参数。
  7. 答案: ${#address}
    • 解析: ${#variable} 是获取字符串长度的标准语法。
  8. 答案: ${version_str:4:5}
    解析:索引4 (第5个字符) 开始,提取5个字符。
  9. 答案: ${path_var//:/ }
    解析: 双斜杠 // 表示全局替换 (替换所有匹配项)。
  10. 答案:报错,提示变量是只读的,无法被unset
    解析: readonly 属性保护变量不被修改或删除。
  11. 答案: my_var="value"echo 输出 valueunset my_varecho 输出一个空行
    解析: unset 彻底移除了变量,而不仅仅是将其值设为空。
  12. 答案:
export API_TOKEN="your_token_here"
python my_script.py
  • 解析: 必须先使用 exportAPI_TOKEN 提升为环境变量,这样 python 这个子进程才能继承并访问它。
  1. 答案: 会输出当前正在执行 echo 命令的那个 Shell 进程的进程ID (PID)
  2. 答案: ${full_path##*/}
    解析: ##*/ 表示从字符串开头 (##) 删除最长匹配 */ (任何字符直到最后一个斜杠) 的部分。
  3. 答案: ${full_path%/*}
    解析: %/* 表示从字符串结尾 (%) 删除最短匹配 /* (一个斜杠及后面的所有字符) 的部分。
http://www.lryc.cn/news/614922.html

相关文章:

  • ESP32之wifi_HTTP
  • 商品、股指及ETF期权五档盘口Tick级与分钟级历史行情数据多维解析
  • 网盘短剧资源转存项目源码 支持垮克 带后台 附教程
  • 深入解析 Apache APISIX 在微服务网关中的性能优化实践指南
  • LeetCode 面试经典 150_数组/字符串_分发糖果(15_135_C++_困难)(贪心算法)
  • Swift 实战:秒算两个数组的交集(LeetCode 349)
  • 海康威视摄像头实时推流到阿里云公网服务器(Windows + FFmpeg + nginx-rtmp)
  • 基于开源AI大模型、AI智能名片与S2B2C商城小程序的零售智能化升级路径研究
  • Selenium使用超全指南
  • Linux运维新手的修炼手扎之第27天
  • 【无标题】AI 赋能日常效率:实用案例与操作心得分享
  • vulhub-Beelzebub靶机
  • 【LeetCode 热题 100】(五)普通数组
  • 版本控制的详细说明介绍(已有github账号版)
  • 【数学归纳法】证明数列极限
  • 模拟人脑处理文本——从分句到分词,从段落到时间线叙事
  • 小米开源大模型 MiDashengLM-7B:不仅是“听懂”,更能“理解”声音
  • 力扣前200题字符串总结
  • Effective C++ 条款31: 将文件间的编译依存关系降至最低
  • Matlab系列(004) 一 Matlab分析正态分布(高斯分布)
  • DBSCAN聚类算法实战全解析
  • 制作 VSCode 插件
  • React Native jpush-react-native极光推送 iOS生产环境接收不到推送
  • 计算机网络:如何将/22的CIDR地址块划分为4个子网
  • 华数杯C题:可调控生物节律的LED光源研究——数学建模与Python实战
  • 2025年华数杯评审标准发布
  • 2025华数杯B题一等奖方案:网络切片无线资源管理全解析(附Python/MATLAB代码)
  • 计算机网络1-6:计算机网络体系结构
  • 4深度学习Pytorch-神经网络--损失函数(sigmoid、Tanh、ReLU、LReLu、softmax)
  • 等保测评-RabbitMQ中间件