shell编程

一、shell脚本

(一)shell基本结构

文件扩展名:通常习惯用 .sh 作为扩展名,不是强制要求。关键在于文件开头的 shebang 行。

shebang 行:一般写成 #!/bin/bash ,这行的作用是指定执行脚本的shell 解释器路径。

(二)注释规范

单行注释:以 # 开头,# 后面的是注释内容;

多行注释:在 Bash 中可以用 <<EOF 和 EOF 包裹起来(类似C语言的/* */);

(三)shell脚本执行方式

1. 赋予执行权限后直接运行:chmod +x xxx.sh && ./xxx.sh

例如:chmod +x demo.sh && ./demo.sh == chmod +x demo.s、 ./demo.sh

2. shell解释器运行:bash xxx.sh 或者是 sh xxx.sh

3. 使用source 执行:source xxx.sh 或者是 . xxx.sh (.是source的简写)

二、变量定义与使用

(一)变量的定义

命名规则:变量名由字母、数字和下划线组成,且不能以数字开头

赋值格式:(等号左右两边不允许有空格)

  • 变量名=初始值
  • 变量名=' 初始值'
  • 变量名="初始值"

注意:当变量的初始值包含空格时,需用单引号或双引号括起来,否则会报错。

在shell中定义的变量没有数据类型,变量的值都视为字符串;如:num=12345678;

变量调用方式:使用 ${变量名} 形式来引用变量的值,可避免歧义。例如:str2="${str1}world",将str1后面加上world,整体赋值给str2。

(二)变量的类型

系统变量

由系统自动设置,提供有关系统和 shell 环境的信息,可在所有脚本中访问。

示例:$HOME(用户主目录)、$USER(当前用户名)等;可使用 echo $变量名 或 set 命令查看。

用户变量

由用户在脚本中创建并赋值,仅在当前 shell 脚本或会话中有效。

赋值与使用:变量名=值,引用时使用 ${变量名} ;删除用 unset 变量名;

环境变量

可在当前 shell 进程及其子进程中都能访问的变量。

声明方法:使用 export 变量名=值 (变量名=值 、export 变量名)。

局部变量

仅在当前 shell 进程或脚本中有效,未export 的用户变量默认为隐式局部变量;局部变量只在函数内部有效,函数执行完毕后,局部变量就会被销毁。

(三)特殊变量与位置参数

特殊变量

$0:表示当前脚本的名称;

$?:表示上一条命令的退出状态码,0 表示命令执行成功,非 0 表示命令执行失败;

位置参数

执行脚本时可传递参数,在脚本中通过特定变量获取;

$1 - $9:依次对应执行脚本时传递的第 1 - 9 个参数;10及以上:如 {10} 表示第 10 个参数,用于获取序号大于 9 的参数;

$*:包含执行脚本时传递的所有参数(不含脚本文件名),循环中会将所有参数当作一个整体处理。

$@:同样表示执行脚本时传递的所有参数(不含脚本文件名),循环中会逐个处理每个参数。

$#:表示执行脚本时传递的参数个数(不含脚本文件名)。

chmod +x demo.sh && ./demo.sh 10 20

(四)修饰变量的关键字

readonly 关键字

定义:用于修饰变量,使其成为只读属性,赋值后不可再修改。

语法:readonly 变量名=值,或先赋值再 readonly 变量名:变量名=值、readonly 变量名;

应用场景:定义程序中的常量,如配置文件路径、版本号;保护关键变量不被意外修改。

注意:只读变量不能使用 unset 删除重新定义同名变量也无法覆盖只读属性

unset 关键字

定义:用于取消变量的定义,删除后变量引用返回空值。

语法:unset 变量名

应用场景:释放不再使用的变量,节省内存;清除包含敏感信息的变量(如密码)。

注意:对 readonly 修饰的变量使用 unset 会报错;若变量未定义,使用 unset 不会报错(对同一个变量多次使用 unset 不会报错)

local 关键字

定义:在函数内部使用,用于创建局部变量,仅在函数内有效

语法:function 函数名() { local 变量名=值; }

应用场景:避免函数内变量污染全局作用域;保护函数内部的临时数据。

注意:函数外使用 local 会报错。

三、算术运算

(一)expr 命令(不常用)

expr 操作数1 运算符 操作数2:操作数与运算符间需空格,部分运算符(如*)需转义,例如expr 5 \* 3

(二)双括号((...))

((变量= 表达式)):支持C 风格运算符(如++、+= 等),可直接修改变量值((a = 5 + 3))

(三)$((...))

$((表达式)):用于直接替换算术表达式结果,类似expr 但语法更现代,无需空格,支持常见算术运算符,如:echo $((5 + 3))

四、数组

在 shell 中,数组仅支持一维,无需预先定义大小,元素可为字符串、数字等任意类型,元素间用空格分隔,索引默认从 0 开始。

赋值:

  • 直接赋值法:通过括号包裹元素列表赋值数组名=(元素1 元素2 元素3 ...)
  • 逐个赋值法:为指定索引的元素赋值数组名[索引]=元素

访问单个元素:用 ${数组名[索引]} 获取指定索引的元素(索引可为数字或变量);

访问所有元素:

  • ${数组名[@]}:将每个元素作为独立参数,适合循环遍历;
  • ${数组名[*]}:将所有元素作为整体字符串(元素间用默认空格分隔);
  • 获取数组长度:用 ${#数组名[@]}${#数组名[*]} 获取元素个数;

末尾添加:自动在最大索引 +1 处添加元素;数组名+=("新元素")

指定索引添加:为指定索引赋值(不存在则创建新元素)数组名[新索引]="新元素"

修改元素:通过索引重新赋值;数组名[索引]="新值"

删除元素:用 unset 删除指定索引元素(数组变稀疏,索引不重排);unset 数组名[索引]

截取数组:获取指定范围元素;${数组名[@]:起始索引:长度}(长度可选,缺省则到末尾

五、条件判断

(一)if ((表达式))

1. if

if ((表达式)); then
        shell语句
fi

当 ((表达式)) 的结果为非零值(即条件为真)时,执行then后的语句;((表达式))是bash特有的算术表达式,支持+、-、*、/、>、<等运算符。

2. if-else

if ((表达式)); then
        shell语句1
else
        shell语句2
fi

当表达式为真时,执行语句1;否则执行shell语句2;

3. if-elif-else

if ((表达式1)); then
        shell语句1
elif ((表达式2)); then
        shell语句2
elif ((表达式3)); then
        shell语句3
        ...
else
        shell语句4
fi

按顺序检查每个表达式,一旦某个表达式为真,则执行对应的语句块,并跳过后续所有elif和else;如果所有表达式都为假,此时有else,则执行else后的语句;没有else,则直接退出程序。

(二)test命令

test 表达式或 [ 表达式 ](本质上是调用 test 命令)。

注意:使用方括号 [ ] 这种形式时,在表达式中,参数、运算符以及中括号前后都必须添加空格方括号与表达式之间也必须保留空格;变量需要用双引号引起来,以防止变量为空或包含空格时出现问题。

test 命令的返回状态码为 0 表示条件成立,非 0 表示条件不成立。当 test 命令执行完毕后,退出码为 0 时,if 判定条件成立,执行 then;当退出码不为 0 时,if 判定条件失败,执行 else。

(三)if与test联合使用 if [ ]

1. 数值判断

用于比较两个整数的大小关系,返回状态码0 (真)或1 (假);脚本中需要比较数字大小的逻辑(如判断年龄、版本号等等);循环条件(如while [ $i -le 10 ]);判断条件(如if [ "$n" -eq 10 ])。

2. 字符串判断

test 配合 if 使用可以用于判断字符串的状态(是否为空、非空),比较字符串间是否相等、大小关系等;返回状态码 0 (真) 或 1 (假)。

-z:如果 string 的长度为0(即空字符串),则条件为真,如:-z "$str"→ 假(str 非空)。

-n :如果 string 的长度大于0(即非空字符串),则条件为真,如:-n "$str"→ 真(str 非空)。

=/== :如果两个字符串相等,则条件为真。= 和 == 等价,如:"$str" =/== "hello"→ 真

!= :如果两个字符串不相等,则条件为真,如:"$str" != "world"→ 真

'>' (需要转义):如果str1 按字典序大于 str2,则条件为真;如:"$str" '>' "abc"→ 真

'<' (需要转义):如果str1 按字典序小于 str2,则条件为真,如:"$str" '<' "world"→ 真

>、< 都需要转义,可以使用 '>' 或 \> 转义。

3. 逻辑判断

逻辑判断用于组合多个条件表达式,形成更复杂的判断。(假设 a=10, b=20)

-a:逻辑与,同时都满足,如:[ "$a" -gt 5 -a "$b" -lt 30 ] (a>5且b<30,为真)

-o:逻辑或,满足其一即可,如:[ "$a" -eq 0 -o "$b" -eq 20 ](真)

! :逻辑非,取反,如:[ ! -d "$dir" ] → 若目录不存在则为真

4. 文件判断

用于检查文件或目录的状态(是否存在、类型、权限等),返回状态码 0 (真) 或 1 (假);

注意:始终用双引号包裹变量:防止路径或变量值包含空格、通配符时被shell错误解析。

(四)if [[  ]]

if [[ 条件表达式1 ]]; then
        shell语句1
elif [[ 条件表达式2 ]]; then
        shell语句2
else
        shell语句3
fi

无需转义特殊字符,语法更简洁:在 [[ ... ]] 中,传统 [ ... ] 需要转义的特殊符号(如 >、<、!等)可直接使用,无需添加反斜杠 \ 或引号包裹

支持短路求值,提升执行效率:逻辑操作符 &&(逻辑与)和 ||(逻辑或)具备短路特性。

  • 当 && 左侧条件为假时,右侧条件不再执行;
  • 当 || 左侧条件为真时,右侧条件不再执行;

安全处理未定义变量:若变量未定义,[[ ... ]] 会将其视为空字符串,避免 [ ... ] 可能触发的语法错误。

(五)case...in(类switch)

case...in 是多路分支结构,用于根据不同的条件执行相应的代码块。类 switch - case 语句。

case 变量 in
        模式1)
                命令序列1
                ;;
        模式2)
                命令序列2
                ;;
        *)  #其他情况
                默认命令序列
                ;;
esac

(六)select...in

select...in :用于创建交互式菜单的专用命令,主要用于引导用户通过键盘输入选择预设选项。它会自动为每个选项生成编号(从 1 开始),用户输入对应数字即可触发相应操作,适合需要频繁与用户交互的场景(如脚本配置、功能选择等)。

select 变量名 in 选项1 选项2 选项3...
do
        命令序列
        [break] # 可选,用于退出循环
done

六、循环结构

(一)for

适用于已知要遍历的元素集合(如数组、列表)或明确循环次数的场景,通过依次迭代集合元素或按指定数字范围来执行循环体。遍历目录下文件、对数组元素逐一操作、按指定次数执行任务等。

遍历列表:
for 变量 in 列表或数组元素; do
        命令1
        命令2
        ...
done

数字范围循环:
for ((初始值; 条件; 增量或减量)); do
        命令1
        命令2
        ...
done

(二)while

根据条件判断结果决定是否执行循环体,只要条件为真就持续循环(返回状态码 0),直到条件为假时结束,适用于条件动态变化或不确定循环次数的情况。

while 条件判断; do
        命令1
        命令2
        ...
done

(三)until

与while循环相反,until循环基于条件判断结果决定是否执行循环体,不过是条件为假时持续循环,一旦条件变为真就结束循环,适用于需要等待某个条件达成的场景;

until 条件判断; do
        命令1
        命令2
        ...
done

在需要等待特定条件从假变为真的场景中,逻辑表达更自然清晰,避免在while循环中使用双重否定条件(例如不必写while [ ! -f file.txt ],而是直接用until [ -f file.txt ])。

七、函数

(一)函数语法格式

标准格式:
function 函数名() {
        命令1
        命令2
        ...
        [return 返回值] # 可选,返回值范围0-255
}

简化格式(省略function关键字):
函数名() {
        命令1
        命令2
        ...
}

(二)函数调用与参数

直接使用函数名即可调用,若函数需要参数,直接跟在函数名后面,参数之间用空格分隔。

参数传递:在 shell 中,函数通过位置参数接收传递给它的参数。
$1, $2, ..., $n:表示第 1 到第 n 个参数。
$0:表示脚本本身的名称,在函数内部使用时,它仍然表示脚本名,而不是函数名。
$#:表示参数的个数。  $@或$*:表示所有参数。
$@将参数视为独立的个体。  $*将所有参数视为一个整体。

通过return语句返回状态码:设置函数的退出状态码(范围 0-255),用于表示函数执行的成功(0)或失败(非 0)。状态码可通过 $? 变量获取,但仅能是整数;return语句会立即终止函数执行,类似exit但仅作用于函数。return 的值就是 $? 的值,范围 0~255。

(三)函数输出捕获

函数输出捕获是获取函数实际计算结果(如字符串、数值等)的手段;

return 语句(仅用于返回状态码):设置函数退出状态码,需结合$?获取;仅能返回整数(0-255);主要用于表示执行状态,而非数据结果。

使用 echo 输出 + 命令替换捕获:通过echo将结果输出到标准输出,再用 $(...) (常用)或反引号`捕获;支持任意类型数据(字符串、数字、多行内容)。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值