【Linux】Shell编程(一):扩展与脚本编写基础

本文内容主要针对bash shell,不同shell之间可能存在差异。

一、shell扩展

Shell 在执行命令前,会对用户输入的内容进行一系列自动转换,这些转换操作统称为 Shell 扩展。主要作用是简化命令输入、增强脚本灵活性,并减少重复性操作。常见的扩展有大括号扩展波浪号扩展变量扩展命令替换文件名扩展

Shell 会按照特定顺序依次尝试这些扩展。如果扩展有效(匹配到内容),则进行替换;否则,保持原样(当作普通字符串)不报错。这种机制需要注意潜在的非预期行为,例如未匹配的扩展可能导致参数传递错误。

1.1 防扩展措施

字符:使用Bash shell 中的转义字符——反斜杠( \ ) ,可以防止其后的单个字符被扩展。

字符串:使用单引号 (' ') 双引号 (" ") 括起字符串。单引号可以阻止所有 shell 扩展,内容均按字面意义处理。双引号允许变量扩展( ${var} )和命令替换( $(cmd) ),但禁止其他扩展(如文件名扩展 、大括号扩展 {})。

1.2 文件名扩展

文件名扩展(也称为通配符扩展 )通过使用特殊字符(通配符)快速匹配文件和目录名,无需手动输入完整名称。当用户在命令参数中使用这些通配符时,Shell会自动将其展开为所有符合匹配规则的实际文件名列表。

模式匹配常用元字符

模式匹配项
*零个或更多字符组成的任意字符串
?任意一个字符
[abc…]位于方括号之间的任意一个字符
[!abc…]不在方括号之间的任意一个字符
[^abc…]与上一行等价,!和 ^ 都表示不在
[[:alpha:]]任意字母字符(不区分大小写)
[[:lower:]]任意小写字符
[[:upper:]]任意大写字符
[[:alnum:]]任意字母字符或数字

示例

可以自己尝试一下每种情况,以及混搭的效果。以下只是简单示例:

ls ??????		# 文件名为6个字符的文件

ls [at]*		# 以 a t 开头的文件
ls *.sh			# 以 .sh 结尾的文件

ls [!a..mA..M]*			# 列出当前目录中文件名不以字母 a/A 到 m/M 开头的所有文件

1.3 大括号扩展

大括号扩展用于生成字符串。

# 双点 (..)表示扩展为一个序列
# echo会将内容输出到终端
echo {1..5}       # 等价于 echo {1,2,3,4,5}
echo {a..c}       # 等价于 echo {a,b,c}

支持组合使用,比如 {A,B,C}{1,2} 会生成"A1 A2 B1 B2 C1 C2",便于创建目录或批量操作文件。以下是一个示例

目前有五部电视剧,分别叫做Good_TV_Drama1,Good_TV_Drama2,Good_TV_Drama3,Good_TV_Drama4,Good_TV_Drama5,每部电视剧都有一个目录用于存放剧集,每部1-15集。

不使用大括号扩展的如下:

mkdir Good_TV_Drama1
cd  Good_TV_Drama1
touch Episode01, Episode02, Episode03, Episode04, Episode05, Episode06, Episode07, Episode08, Episode09, Episode10, Episode11, Episode12, Episode13, Episode14, Episode15
cd ..
# 2,3,4重复上述操作

# 当然,这种重复性的操作可以采用循环(脚本编写部分介绍)
for i in 1 2 3 4 5; do
    mkdir "Good_TV_Drama$i"
    cd "Good_TV_Drama$i" || exit
    touch Episode{01..15}
    cd ..
done

以下是采用大括号扩展,工作量骤减:

mkdir Good_TV_Drama{1..5}
touch Good_TV_Drama{1..5}/Episode{01..15}

# 也可以合并到一行
mkdir Good_TV_Drama{1..5} && touch Good_TV_Drama{1..5}/Episode{01..15}

1.4 波浪号扩展

波浪号~可扩展为当前用户的家目录,就像手机的home键。

~        # 当前用户家目录
~user    # 指定用户的家目录

常见用法如下:

# 用法不止于此,涉及需要对家目录(/home/用户名)的操作,都可以使用 ~ 代替
cd ~				# 回到家目录
cd ~/dir			# 进入家目录下的dir目录中

ls -l ~/			# 查看家目录下的信息
ls ~username  		# 查看指定用户的家目录(前提是要有权限)

1.5 变量扩展

变量扩展可以将变量名替换为存储的值。当 Shell 遇到以美元符号($开头的字符串时,会将其识别为变量引用,并尝试查找对应的变量值进行替换。例如,$HOME 会被扩展为当前用户的家目录路径,$PATH 会被拓展为环境变量。

# 查看当前环境变量的值
echo ${PATH}                # ${PATH}等价于波浪号~拓展

为了确保变量扩展的准确性,建议始终用花括号({})包裹变量名。这种方式能明确界定变量名的边界,避免歧义。

Shell 变量命名遵循以下规则:

  • 允许的字符包括字母(区分大小写)、数字和下划线(_)。
  • 变量名不能以数字开头,如 6var 是无效的。
  • 变量名区分大小写,因此 $PATH 和 $Path 是两个不同的变量。

这部分内容可以查看这篇文章:
How To Use Bash Parameter Substitution Like A Pro - nixCrafthttps://www.cyberciti.biz/tips/bash-shell-parameter-substitution-2.html

1.5.1 变量定义

# 定义变量(等号两边不能有空格)
variable_name="value"

# 定义数字变量
count=10

# 定义数组
array=("apple" "banana" "cherry")

1.5.2 变量使用

${var} 				# var替换为具体变量名
# 以下了解,需要时可查看上面的文章
${var#prefix}   	# 删除最短匹配前缀
${var##prefix}  	# 删除最长匹配前缀
${var%suffix}   	# 删除最短匹配后缀
${var%%suffix}  	# 删除最长匹配后缀
${var/old/new}  	# 替换第一个匹配
${var//old/new} 	# 替换所有匹配
${var:start:len}	# 子字符串提取

后期如果有遇到常用的,会进行补充。

1.6 命令替换

命令替换将一个命令的输出结果直接嵌入到另一个命令行中使用。当使用 $(command) 语法时,Shell 会先执行括号内的命令,然后将其标准输出内容替换到原命令位置,其支持多级嵌套,比如 $(command1$(command2)) 。

$(command)			# command替换为具体命令
`command`			# 较旧的格式,使用反引号,不推荐,容易和单引号(') 弄混 

注:反引号 ( ` )是英文模式下键盘左上角 Esc 键下方的按键。

# 将日期嵌入到文件名中
backup_file="backup-$(date +%Y%m%d).tar.gz"
echo "备份文件名: $backup_file"

补充:变量扩展和命令替换都是以美元符号($)开头的,系统如何区分呢?如果同名呢?

答:变量扩展优先级高于命令替换,在同名且未使用 () 或者 {} 时,系统优先匹配变量,未找到的情况下匹配命令替换。使用时最好带上 () 或者 {}。

二、shell脚本编写

Shell脚本是用Shell命令编写的可执行文件,本质上是一系列Linux/Unix命令的集合,包含Shell命令和逻辑控制语句,能够自动化执行系统任务,告诉系统按特定顺序执行各种命令,适合完成系统管理、文件处理和日常自动化工作。

特点:

  • 直接调用系统原生命令,系统管理效率高
  • 无需编译即可运行

局限性

  • 不适合复杂计算或大型应用
  • 性能不如编译型语言
  • 不同Shell版本兼容性问题

2.1 指定解释器

shell脚本的第一行#!称为she-bang(也称hash-bang),由 #(hash/sharp)和 !(bang)组成,用于指定脚本的解释器路径。当运行脚本时,系统内核会读取这行指令,自动调用对应的解释器来执行脚本内容。

# 使用Bash执行该脚本
#!/usr/bin/bash       # 直接指定系统的 bash 路径
#!/usr/bin/env bash   # 通过 env 查找 bash(更好的可移植性)

2.2 脚本代码结构

以下内容和其他语言(c/c++等)的结构很类似,所以介绍的较为简略,可以在后续章节的具体示例中查看用法。

2.2.1 if条件判断

# 以下的空格不能省略,不能是[[条件]]
if [[ 条件 ]]; then
    # 条件成立时执行的代码
elif [[ 条件 ]]; then
    # 其他条件成立时执行的代码
else
    # 所有条件均不成立时执行的代码
fi

注:[ ]或 [[ ]] 用于条件测试,操作符和操作数之间必须有空格。推荐 [[ ]] ,其支持字符串模式匹配、逻辑运算符组合、正则表达式等。

结构

用途

支持的数据类型

(( ))

算术运算

整数(直接支持)

[[ ]]

条件测试

字符串/文件/布尔逻辑

2.2.2 条件判断运算符

数值比较操作符
  • -eq (equal) - 等于

  • -ne (not equal) - 不等于

  • -lt (less than) - 小于

  • -gt (greater than) - 大于

  • -le (less or equal) - 小于等于

  • -ge (great or equal) - 大于等于

字符串比较操作符
  • == 或 = - 字符串相等

  • != - 字符串不等

  • -z - 字符串长度为零(空字符串)

  • -n - 字符串长度非零(非空字符串)

文件测试操作符
  • -e - 文件/目录是否存在

  • -f - 是普通文件

  • -d - 是目录

  • -L - 是符号链接

  • -r - 可读

  • -w - 可写

  • -x - 可执行

  • -s - 文件大小大于零(非空)

2.2.3 for循环

for 变量 in 列表; do
    # 循环体
done

2.2.4 while循环

while [[ 条件 ]]; do
    # 循环体(条件成立时执行)
done

2.2.5 case分支选择

case 变量 in
    情况1)
        # 情况1对应代码

        ;;
    情况2)
        # 情况2对应代码

        ;;
    *)
        # 默认情况(可选)

        ;;
esac

2.3 执行 bash 脚本

1. 执行之前需要先确定脚本的所在路径

        1)若脚本位于shell的PATH环境变量列出的目录中,则可以直接使用文件名运行shell脚本。需要注意的是PATH 解析会运行匹配的第一个文件名,因此不要使用现有的命令名称来命名脚本文件。

使用 which 命令可以查询文件所在路径:

注意:which 命令只能查找 PATH 环境变量所列目录中的可执行文件,别名(比如,ll )和Shell 内建命令(比如,cd )或者不在PATH中的无法找到。

which [文件名]
which tree                # tree是一个显示目录结构的工具

        2)如果脚本不在 PATH 目录中,则需要使用绝对路径或者相对路径名称运行。

2. 具体的执行方式有两种,一种是作为用户执行,另一种是使用bash命令执行。

        1)作为用户执行时,无论是否在环境变量中都需要有脚本的执行(x)读取(r)权限。(权限相关的文章正在完善中,后期会在此处放链接)

通过ls命令可以看出缺少执行(x)权限,脚本无法运行。添加之后脚本可正常运行。

chmod a+x test_auth.sh
./test_auth.sh                # 脚本需在当前目录下

        2)使用bash命令执行时会强制使用bash解释器执行,忽略脚本自身的shebang,所以不需要脚本具有执行权限(但仍需读取权限)。

bash [脚本名]
bash test_auth.sh

2.4 bash 脚本退出代码

Shell脚本的退出代码机制与其他编程语言(如C/C++)中的函数返回值的概念类似。以c/c++为例,在函数中常通过返回值判断函数的执行情况。这里的脚本退出代码也是同理,用来判断脚本的执行情况。正常情况下为0,表示成功,非0表示失败,具体非0值的含义由脚本具体内容决定,通常用于区分不同类型的错误。

比如以下代码,在找不到日志文件时,返回1。

使用 $? 特殊变量可以获取上一个命令/脚本的退出状态。

echo $?


预告:本系列下一篇文章为grep、sed、awk三剑客详解

如有问题或建议,欢迎在评论区中补充~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值