linux shell编程实战 02 变量与交互式输入

2.1 变量:Shell编程的基本单元

变量是任何编程语言的基础,Shell也不例外。在Shell中,变量用于存储数据,这些数据可以是数字、字符串、路径等。理解变量的定义、使用和特性,是掌握Shell编程的第一步。

2.1.1 变量的定义与赋值

Shell变量的定义非常简单,不需要声明类型,直接使用变量名=值的形式即可。

基本语法

bash 复制代码
变量名=值

注意事项

  • 变量名和等号之间不能有空格(这是初学者最容易犯的错误)
  • 变量名只能包含字母、数字和下划线,且不能以数字开头
  • 变量名区分大小写(nameName是两个不同的变量)

示例

bash 复制代码
# 定义字符串变量
name="张三"
address='北京市海淀区'

# 定义数字变量
age=25
score=95.5

# 定义路径变量
log_path="/var/log/syslog"

# 错误示例(会导致语法错误)
# name = "李四"  # 等号两边有空格
# 123num=100     # 以数字开头
# my-name=test   # 包含非法字符"-"

2.1.2 变量的使用(引用变量)

使用变量时,需要在变量名前加$符号,这称为变量引用

基本语法

bash 复制代码
$变量名
${变量名}  # 推荐使用这种形式,避免歧义

示例

bash 复制代码
name="张三"
echo $name       # 输出:张三
echo ${name}     # 输出:张三(推荐用法)

# 变量与其他文本拼接
echo "姓名:${name}同学"  # 输出:姓名:张三同学

# 不使用{}的问题
echo "姓名:$name同学"    # 也能输出正确结果,但不推荐
echo "年龄:$age岁"      # 输出:年龄:25岁

# 必须使用{}的场景
user="admin"
echo "用户名:${user}123"  # 输出:用户名:admin123
echo "用户名:$user123"    # 错误:会查找变量user123(不存在),输出空

为什么推荐使用 ${变量名}形式?

  • 避免变量名与后面的文本混淆(如上例中的${user}123
  • 使变量引用更清晰,提高脚本可读性

2.1.3 变量的重新赋值与删除

变量定义后可以重新赋值,也可以用unset命令删除变量。

示例

bash 复制代码
# 定义变量
count=10
echo $count  # 输出:10

# 重新赋值
count=20
echo $count  # 输出:20

# 删除变量
unset count
echo $count  # 输出空(变量已不存在)

2.1.4 从命令输出赋值(命令替换)

变量可以通过命令替换获取命令的输出结果,有两种语法形式:

  1. 变量名=$(命令)(推荐,可读性好)
  2. 变量名=命令``(反引号,兼容性稍差)

示例

bash 复制代码
# 获取当前日期
today=$(date +%Y-%m-%d)
echo "今天是:${today}"  # 输出:今天是:2023-10-16

# 获取当前目录下的文件数
file_count=$(ls | wc -l)
echo "当前目录有${file_count}个文件"

# 反引号形式(功能相同)
current_dir=`pwd`
echo "当前目录:${current_dir}"

注意:命令替换中的命令会在子进程中执行,其环境变量不会影响当前脚本。

2.2 引号的区别:单引号、双引号与反引号

在Shell中,引号的使用非常重要,不同的引号会对变量和特殊字符产生不同的处理方式。掌握引号的区别,能避免很多常见错误。

2.2.1 单引号(''):强引用

单引号会原样保留其中的所有内容,不解析变量,也不处理特殊字符(除了单引号本身)。

特点

  • 变量引用(如$name)不会被替换为变量值
  • 特殊字符(如*?$)会被当作普通字符
  • 单引号中不能包含单引号(即使转义也不行)

示例

bash 复制代码
name="张三"

# 单引号中的变量不会被解析
echo '姓名:$name'  # 输出:姓名:$name(而不是预期的"姓名:张三")

# 单引号中的特殊字符被当作普通字符
echo '当前目录:$(pwd)'  # 输出:当前目录:$(pwd)
echo '列出文件:ls *.txt' # 输出:列出文件:ls *.txt

适用场景

  • 当需要输出纯文本,不希望任何变量或命令被解析时
  • 包含特殊字符的字符串,如正则表达式、路径等

2.2.2 双引号(""):弱引用

双引号会保留大部分内容,但会解析变量和命令替换,同时忽略大部分特殊字符的含义。

特点

  • 变量引用(如$name)会被替换为变量值
  • 命令替换(如$(date))会被执行并替换为结果
  • 大部分特殊字符(如*?)会被当作普通字符
  • 可以包含单引号,单引号在双引号中被当作普通字符

示例

bash 复制代码
name="张三"

# 双引号中的变量会被解析
echo "姓名:$name"  # 输出:姓名:张三

# 双引号中的命令替换会被执行
echo "今天是:$(date +%Y-%m-%d)"  # 输出:今天是:2023-10-16

# 双引号中的特殊字符被当作普通字符
echo "列出文件:ls *.txt"  # 输出:列出文件:ls *.txt(不会执行ls命令)

# 双引号中可以包含单引号
echo "他说:'Hello World'"  # 输出:他说:'Hello World'

适用场景

  • 包含变量或命令替换的字符串
  • 需要保留空格和换行符的文本
  • 大多数需要解析变量的场景(最常用的引号)

2.2.3 反引号(``):命令替换

反引号的作用是命令替换 ,与$(命令)功能相同,用于将命令的输出结果赋值给变量或嵌入到字符串中。

特点

  • 会执行其中的命令,并将输出结果替换到当前位置
  • 不推荐在嵌套命令中使用(可读性差)

示例

bash 复制代码
# 反引号与$()功能相同
current_time=`date +%H:%M:%S`
echo "当前时间:${current_time}"  # 输出:当前时间:15:30:45

# 嵌套场景(推荐使用$())
# 不推荐:统计当前目录下的txt文件数
txt_count=`ls *.txt | wc -l`
echo "txt文件数:${txt_count}"

# 推荐:嵌套更清晰
txt_count=$(ls *.txt | wc -l)
echo "txt文件数:${txt_count}"

注意 :反引号在嵌套使用时需要转义,而$(命令)不需要,因此推荐使用 $(命令)形式

2.2.4 无引号:特殊处理

当字符串不使用引号时,Shell会进行分词路径扩展(通配符匹配)。

特点

  • 会解析变量和命令替换
  • 多个空格会被合并为一个空格
  • 会进行路径扩展(*?等通配符会被展开)
  • 特殊字符(如;&)会被解析为命令分隔符

示例

bash 复制代码
name="张三"
# 无引号时变量会被解析
echo 姓名:$name  # 输出:姓名:张三

# 多个空格会被合并
echo 张三   李四  # 输出:张三 李四(多个空格合并为一个)

# 通配符会被展开
echo *.sh  # 输出当前目录下所有.sh文件(如:hello.sh system_info.sh)

# 特殊字符会被解析
echo hello; echo world  # 会被解析为两个命令,输出hello和world

适用场景

  • 简单的变量引用或命令
  • 明确需要路径扩展的场景(如ls *.txt

2.2.5 引号使用对比表

引号类型 变量解析 命令替换 空格保留 通配符扩展 特殊字符处理
单引号'' 不解析 不执行 保留 不扩展 当作普通字符
双引号"" 解析 执行 保留 不扩展 当作普通字符
无引号 解析 执行 合并 扩展 解析为特殊含义

最佳实践

  • 大多数情况下使用双引号(平衡了灵活性和安全性)
  • 当需要原样输出时使用单引号
  • 命令替换优先使用$(命令)而非反引号
  • 避免在复杂场景中使用无引号

2.3 特殊变量:Shell内置的"信息载体"

Shell提供了一系列预定义的特殊变量,它们用于存储脚本执行过程中的各种信息,如参数个数、脚本名称、命令返回值等。这些变量在脚本编写中非常常用,必须熟练掌握。

2.3.1 位置参数:1, n

位置参数用于获取脚本或函数的参数,$0表示脚本名称,$1表示第一个参数,$2表示第二个参数,以此类推,$n表示第n个参数。

示例 :创建params_demo.sh脚本

bash 复制代码
#!/bin/bash
echo "脚本名称:$0"
echo "第一个参数:$1"
echo "第二个参数:$2"
echo "第三个参数:$3"

运行脚本并传递参数:

bash 复制代码
chmod +x params_demo.sh
./params_demo.sh 张三 25 男

输出结果:

plain 复制代码
脚本名称:./params_demo.sh
第一个参数:张三
第二个参数:25
第三个参数:男

注意

  • $0返回的是脚本的调用路径(如./params_demo.sh/home/user/params_demo.sh
  • 如果参数包含空格,需要用双引号括起来(如./script.sh "张三 李四"

2.3.2 其他常用特殊变量

特殊变量 含义 示例
$# 传递给脚本或函数的参数个数 若传递3个参数,echo $#输出3
$* 所有参数的列表,作为一个整体(用双引号括起来时) 若参数为a b c,echo "$*"输出"a b c"
$@ 所有参数的列表,每个参数单独作为一个元素 若参数为a b c,echo "$@"输出"a" "b" "c"
$? 上一个命令的退出状态码(0表示成功,非0表示失败) 执行成功命令后echo $?输出0
$$ 当前Shell进程的PID(进程ID) echo $$输出当前脚本的进程ID
$! 上一个后台运行命令的PID sleep 10 & echo $!输出sleep命令的PID
$- 当前Shell的选项标志 通常用于调试,echo $-可能输出"hB"等

示例1: #和 ?的使用

bash 复制代码
#!/bin/bash
# 脚本名:special_vars.sh

echo "参数个数:$#"

# 检查参数是否足够
if [ $# -lt 2 ]; then
    echo "错误:至少需要2个参数!"
    exit 1  # 手动设置退出状态码为1(表示错误)
fi

echo "执行成功"
exit 0  # 手动设置退出状态码为0(表示成功)

运行结果:

bash 复制代码
# 传递1个参数(不足)
./special_vars.sh 张三
参数个数:1
错误:至少需要2个参数!
echo $?  # 输出1(上一个命令执行失败)

# 传递2个参数(足够)
./special_vars.sh 张三 25
参数个数:2
执行成功
echo $?  # 输出0(上一个命令执行成功)

示例2: _@的区别_

bash 复制代码
#!/bin/bash
echo "使用\$*遍历参数:"
for arg in "$*"; do
    echo "参数:$arg"
done

echo -e "\n使用\$@遍历参数:"
for arg in "$@"; do
    echo "参数:$arg"
done

运行脚本:./demo.sh a b c

输出结果:

plain 复制代码
使用$*遍历参数:
参数:a b c

使用$@遍历参数:
参数:a
参数:b
参数:c

区别总结

  • "$*"将所有参数视为一个整体字符串
  • "$@"将每个参数视为独立的字符串(更常用,尤其是遍历参数时)

2.3.3 接收超过9个的参数

当参数个数超过9个时,需要用${10}、${11}...的形式引用:

bash 复制代码
#!/bin/bash
echo "第10个参数:${10}"
echo "第11个参数:${11}"

运行脚本:./script.sh 1 2 3 4 5 6 7 8 9 10 11

输出:

plain 复制代码
第10个参数:10
第11个参数:11

2.4 参数传递:向脚本传递数据

在实际应用中,脚本往往需要接收外部输入的参数来完成不同的任务。除了直接传递位置参数,还有其他更灵活的参数处理方式。

2.4.1 基本参数传递

最常用的参数传递方式是在运行脚本时直接指定参数,用空格分隔:

bash 复制代码
# 格式
./脚本名 参数1 参数2 参数3 ...

# 示例
./backup.sh /data /backup 7

在脚本中通过$1、$2、$3获取这些参数,分别对应/data、/backup、7

2.4.2 选项与参数分离(getopts命令)

对于复杂的脚本,通常需要支持选项(如-h、-v、-f filename等),可以使用getopts命令处理这种情况。

基本语法

bash 复制代码
getopts "选项字符串" 变量名
  • 选项字符串中的字母表示支持的选项(如"hvf:"表示支持-h、-v、-f选项)
  • 选项后的冒号:表示该选项需要参数(如"f:"表示-f需要参数)

示例 :创建option_demo.sh

bash 复制代码
#!/bin/bash

# 初始化变量
file=""
verbose=0

# 解析选项
while getopts "hf:v" opt; do
    case $opt in
        h)  # 显示帮助信息
            echo "用法:$0 [选项]..."
            echo "  -h    显示帮助信息"
            echo "  -f    指定文件"
            echo "  -v    显示详细信息"
            exit 0
            ;;
        f)  # 处理-f选项的参数
            file="$OPTARG"
            ;;
        v)  # 处理-v选项
            verbose=1
            ;;
        \?) # 处理无效选项
            echo "错误:无效选项 -$OPTARG" >&2
            exit 1
            ;;
        :)  # 处理缺少参数的选项
            echo "错误:选项 -$OPTARG 需要参数" >&2
            exit 1
            ;;
    esac
done

# 显示处理结果
if [ $verbose -eq 1 ]; then
    echo "详细模式开启"
fi
if [ -n "$file" ]; then
    echo "指定的文件:$file"
fi

运行脚本:

bash 复制代码
# 显示帮助
./option_demo.sh -h

# 使用-f和-v选项
./option_demo.sh -v -f test.txt

输出结果:

plain 复制代码
用法:./option_demo.sh [选项]...
  -h    显示帮助信息
  -f    指定文件
  -v    显示详细信息

详细模式开启
指定的文件:test.txt

常用变量

  • OPTARG:存储当前选项的参数(如-f test.txt中的test.txt
  • OPTIND:下一个要处理的参数索引(用于处理选项后的位置参数)

2.5 read命令:实现交互式输入

read命令用于从标准输入(通常是键盘)读取用户输入,并将输入赋值给变量。它是实现脚本与用户交互的重要工具。

2.5.1 read命令的基本用法

基本语法

bash 复制代码
read 变量名

示例

bash 复制代码
#!/bin/bash
echo "请输入您的姓名:"
read name  # 读取用户输入并赋值给name变量
echo "您好,${name}!"

运行脚本:

plain 复制代码
请输入您的姓名:
张三
您好,张三!

2.5.2 显示提示信息(-p选项)

使用-p选项可以在同一行显示提示信息,不需要单独使用echo

bash 复制代码
#!/bin/bash
read -p "请输入您的年龄:" age  # 直接显示提示并读取输入
echo "您的年龄是:${age}岁"

运行结果:

plain 复制代码
请输入您的年龄:25
您的年龄是:25岁

2.5.3 读取多个变量

read命令可以同时读取多个变量,输入的多个值用空格分隔:

bash 复制代码
#!/bin/bash
read -p "请输入您的姓名和年龄(用空格分隔):" name age
echo "姓名:${name},年龄:${age}岁"

运行结果:

plain 复制代码
请输入您的姓名和年龄(用空格分隔):张三 25
姓名:张三,年龄:25岁

如果输入的值少于变量个数,多余的变量会被赋值为空;如果输入的值多于变量个数,最后一个变量会接收剩余的所有值。

2.5.4 限制输入时间(-t选项)

使用-t选项可以设置输入超时时间(单位:秒),超时后脚本继续执行:

bash 复制代码
#!/bin/bash
if read -t 5 -p "请在5秒内输入您的爱好:" hobby; then
    echo "您的爱好是:${hobby}"
else
    echo -e "\n输入超时!"  # -e启用转义,\n表示换行
fi

运行结果(5秒内未输入):

plain 复制代码
请在5秒内输入您的爱好:
输入超时!

2.5.5 隐藏输入(-s选项)

使用-s选项可以隐藏输入(不显示用户输入的内容),常用于输入密码:

bash 复制代码
#!/bin/bash
read -s -p "请输入密码:" password
echo -e "\n密码已接收(出于安全考虑不显示)"
# 这里可以添加密码验证逻辑

运行结果:

plain 复制代码
请输入密码:
密码已接收(出于安全考虑不显示)

2.5.6 读取整行输入(包括空格)

默认情况下,read会以空格作为分隔符,如果需要读取包含空格的整行输入,可以将输入赋值给一个变量:

bash 复制代码
#!/bin/bash
read -p "请输入一句完整的话:" sentence
echo "您输入的是:${sentence}"

运行结果:

plain 复制代码
请输入一句完整的话:我正在学习Shell编程
您输入的是:我正在学习Shell编程

2.6 环境变量与普通变量:作用域的区别

在Shell中,变量按作用域可以分为普通变量 (局部变量)和环境变量(全局变量),它们的主要区别在于是否能在子进程中被访问。

2.6.1 普通变量(局部变量)

定义:在当前Shell进程中定义的变量,默认只在当前Shell中有效。

特点

  • 只在定义它的Shell进程中可见
  • 子进程(如运行的脚本、其他命令)无法访问
  • 通常用变量名=值的形式定义

示例

bash 复制代码
# 在当前Shell中定义普通变量
name="张三"
echo $name  # 输出:张三

# 运行一个脚本(子进程)
cat > test.sh << 'EOF'
#!/bin/bash
echo "脚本中访问name变量:$name"
EOF
chmod +x test.sh

./test.sh  # 输出:脚本中访问name变量:(空,因为子进程无法访问普通变量)

2.6.2 环境变量(全局变量)

定义:可以在当前Shell及其所有子进程中访问的变量。

特点

  • 在当前Shell和所有子进程中可见
  • 通常用于设置系统级别的配置(如PATH、HOME等)
  • 需要用export命令定义或导出

示例

bash 复制代码
# 方法1:先定义变量,再导出
age=25
export age  # 导出为环境变量

# 方法2:定义并导出(推荐)
export gender="男"

# 在当前Shell中访问
echo $age    # 输出:25
echo $gender # 输出:男

# 在子进程中访问
./test.sh  # 输出:脚本中访问name变量:(仍为空),但可以访问age和gender

修改test.sh内容:

bash 复制代码
#!/bin/bash
echo "脚本中访问age变量:$age"
echo "脚本中访问gender变量:$gender"

再次运行:

bash 复制代码
./test.sh

输出结果:

plain 复制代码
脚本中访问age变量:25
脚本中访问gender变量:男

2.6.3 常见的系统环境变量

Linux系统预设了很多环境变量,用于配置系统行为:

环境变量 含义
PATH 可执行程序的搜索路径,用冒号分隔
HOME 当前用户的家目录(如/home/username
USER 当前登录的用户名
UID 当前用户的UID(用户ID)
PWD 当前工作目录
SHELL 当前使用的Shell路径(如/bin/bash
LANG 系统语言和编码设置
TERM 终端类型
PS1 命令提示符格式

查看环境变量

bash 复制代码
# 查看所有环境变量
env
printenv

# 查看特定环境变量
echo $PATH
echo $HOME

修改环境变量

bash 复制代码
# 临时添加路径到PATH(当前Shell有效)
export PATH=$PATH:/new/directory

# 永久修改(需要重启Shell或重新登录)
echo 'export PATH=$PATH:/new/directory' >> ~/.bashrc
source ~/.bashrc  # 立即生效

2.6.4 变量作用域总结

变量类型 定义方式 作用域 子进程可见性 典型用途
普通变量 var=value 当前Shell 不可见 脚本内部临时存储数据
环境变量 export var=value 当前Shell及所有子进程 可见 传递配置信息到子进程

最佳实践

  • 临时使用的变量用普通变量
  • 需要在子进程中使用的变量用环境变量
  • 避免滥用环境变量(可能导致命名冲突)
  • 系统环境变量(如PATH)修改需谨慎

2.7 变量的运算:算术与字符串操作

Shell虽然不是专门的计算语言,但也支持基本的变量运算,包括算术运算和字符串操作。

2.7.1 算术运算

Shell中的算术运算需要使用特殊的语法,常用的有以下几种方式:

  1. $((表达式))(推荐,最常用)
  2. $[表达式](兼容旧版本,不推荐)
  3. expr 表达式(较古老,语法严格)
  4. let 表达式(适合简单赋值)

支持的运算符

  • +:加法
  • -:减法
  • *:乘法
  • /:除法(整数除法,舍去小数)
  • %:取余(模运算)
  • ++:自增
  • --:自减
  • **:幂运算(bash 4.0+支持)

示例

bash 复制代码
# 方法1:$((表达式))
a=10
b=3
echo "a + b = $((a + b))"  # 输出:a + b = 13
echo "a * b = $((a * b))"  # 输出:a * b = 30
echo "a / b = $((a / b))"  # 输出:a / b = 3(整数除法)
echo "a % b = $((a % b))"  # 输出:a % b = 1
echo "a^2 = $((a **2))"   # 输出:a^2 = 100

# 方法2:let命令
let c=a+b
echo "c = $c"  # 输出:c = 13

let a++  # a自增1
echo "a = $a"  # 输出:a = 11

# 方法3:expr命令(注意空格)
d=$(expr 10 + 5)
echo "d = $d"  # 输出:d = 15

# 注意:乘法需要转义
e=$(expr 10 \* 3)
echo "e = $e"  # 输出:e = 30

2.7.2 字符串操作

Shell支持多种字符串操作,如长度计算、截取、拼接等,不需要特殊命令,直接通过变量引用实现。

1. 字符串长度

bash 复制代码
str="Hello World"
echo "字符串长度:${#str}"  # 输出:字符串长度:11

2. 字符串拼接

bash 复制代码
first="Hello"
last="World"
# 直接拼接(无需运算符)
echo "${first} ${last}"  # 输出:Hello World
echo $first$last        # 输出:HelloWorld(无空格)

# 拼接变量和字符串
echo "${first}, everyone!"  # 输出:Hello, everyone!

3. 字符串截取

bash 复制代码
str="abcdefghijklmn"

# ${变量:起始位置:长度},起始位置从0开始
echo "${str:0:3}"   # 输出:abc(从0开始,取3个字符)
echo "${str:5:2}"   # 输出:fg(从5开始,取2个字符)
echo "${str:7}"     # 输出:hijklmn(从7开始,取剩余所有字符)

# 从末尾开始截取(起始位置为负数)
echo "${str: -3}"   # 输出:lmn(取最后3个字符,注意冒号后有空格)
echo "${str: -5:2}" # 输出:jk(从倒数第5个开始,取2个字符)

4. 字符串替换

bash 复制代码
str="I like apple, apple is good"

# 替换第一个匹配项
echo "${str/apple/orange}"  # 输出:I like orange, apple is good

# 替换所有匹配项
echo "${str//apple/orange}" # 输出:I like orange, orange is good

# 从开头匹配并替换
echo "${str/#I/You}"        # 输出:You like apple, apple is good

# 从结尾匹配并替换
echo "${str/%good/great}"   # 输出:I like apple, apple is great

5. 字符串删除

bash 复制代码
str="abc123def456"

# 删除第一个匹配的数字部分
echo "${str/[0-9]*/}"  # 输出:abc(删除从第一个数字开始的所有内容)

# 删除所有数字
echo "${str//[0-9]/}"  # 输出:abcdef

2.8 实战示例:个人信息收集脚本

综合运用本章所学知识,我们来编写一个个人信息收集脚本,实现以下功能:

  • 接收命令行参数或交互式输入个人信息
  • 验证输入的有效性
  • 格式化输出收集的信息
bash 复制代码
#!/bin/bash
# 文件名:collect_info.sh
# 功能:收集并显示个人信息

# 初始化变量
name=""
age=""
gender=""
email=""

# 函数:显示帮助信息
show_help() {
    echo "用法:$0 [选项]..."
    echo "收集并显示个人信息"
    echo
    echo "选项:"
    echo "  -n 姓名    指定姓名"
    echo "  -a 年龄    指定年龄"
    echo "  -g 性别    指定性别(男/女)"
    echo "  -e 邮箱    指定邮箱地址"
    echo "  -h         显示帮助信息"
    echo
    echo "如果不指定选项,将以交互式方式输入"
}

# 解析命令行选项
while getopts "n:a:g:e:h" opt; do
    case $opt in
        n) name="$OPTARG" ;;
        a) age="$OPTARG" ;;
        g) gender="$OPTARG" ;;
        e) email="$OPTARG" ;;
        h) show_help; exit 0 ;;
        \?) echo "错误:无效选项 -$OPTARG" >&2; exit 1 ;;
        :) echo "错误:选项 -$OPTARG 需要参数" >&2; exit 1 ;;
    esac
done

# 交互式输入缺失的信息
if [ -z "$name" ]; then
    read -p "请输入您的姓名:" name
fi

if [ -z "$age" ]; then
    while true; do
        read -p "请输入您的年龄:" age
        # 验证年龄是否为数字
        if [[ $age =~ ^[0-9]+$ ]] && [ $age -gt 0 ] && [ $age -lt 150 ]; then
            break
        else
            echo "错误:请输入有效的年龄(1-149)"
        fi
    done
fi

if [ -z "$gender" ]; then
    while true; do
        read -p "请输入您的性别(男/女):" gender
        if [ "$gender" = "男" ] || [ "$gender" = "女" ]; then
            break
        else
            echo "错误:请输入'男'或'女'"
        fi
    done
fi

if [ -z "$email" ]; then
    while true; do
        read -p "请输入您的邮箱地址:" email
        # 简单验证邮箱格式
        if [[ $email =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+$ ]]; then
            break
        else
            echo "错误:请输入有效的邮箱地址"
        fi
    done
fi

# 显示收集的信息
echo -e "\n===== 个人信息 ====="
echo "姓名:$name"
echo "年龄:$age 岁"
echo "性别:$gender"
echo "邮箱:$email"
echo "===================="

脚本功能说明

  1. 支持命令行选项(-n-a-g-e)直接传递信息
  2. 对缺失的信息进行交互式询问
  3. 验证年龄(必须是1-149的数字)和邮箱格式(简单正则匹配)
  4. 格式化输出收集的信息

运行示例

bash 复制代码
# 直接运行,全交互式输入
./collect_info.sh

# 部分参数通过命令行传递,部分交互式输入
./collect_info.sh -n 张三 -a 25

小结

本章详细讲解了Shell编程的基础知识,包括:

  • 变量:定义、赋值、引用和删除的方法,以及命令替换的使用
  • 引号:单引号、双引号和反引号的区别及适用场景
  • 特殊变量 :位置参数(1...)、 #、 ?、@等的含义和用法
  • 参数传递:基本参数传递和getopts处理选项的方法
  • read命令:实现交互式输入,包括提示信息、超时设置、隐藏输入等
  • 环境变量与普通变量:作用域的区别和适用场景
  • 变量运算:算术运算和字符串操作的常用方法

这些基础知识是Shell编程的基石,掌握它们对于编写复杂脚本至关重要。在实际应用中,这些概念往往是结合使用的,需要通过大量练习来熟悉。

下一章,我们将学习Shell中的条件判断与流程控制,这将使脚本能够根据不同情况执行不同的操作,实现更复杂的逻辑。

相关推荐
Dovis(誓平步青云)4 小时前
《简易制作 Linux Shell:详细分析原理、设计与实践》
linux·运维·服务器
weixin_307779134 小时前
在Linux服务器上使用Jenkins和Poetry实现Python项目自动化
linux·开发语言·python·自动化·jenkins
爱宇阳4 小时前
Linux 教程:如何查看服务器当前目录中的文件
linux·运维·github
天才奇男子4 小时前
用户管理,权限管理
linux·云原生
wheeldown4 小时前
【Linux】Linux 进程通信:System V 共享内存(最快方案)C++ 封装实战 + 通信案例,4 类经典 Bug 快速修复
linux·运维·服务器·开发语言
Ching·5 小时前
linux系统编程(十②)RK3568 socket之 TCP 客户端的实现
linux·tcp/ip·rk3568
NiKo_W5 小时前
Linux 线程控制
linux·数据结构·内核·线程·进程·线程控制
迎風吹頭髮5 小时前
Linux内核架构浅谈44-Linux slab分配器:通用缓存与专用缓存的创建与使用
linux·spring·架构
AORO20255 小时前
防爆手机与普通手机有什么区别?防爆手机哪个牌子好?
运维·服务器·网络·5g·智能手机·信息与通信