十一,Shell

一,shell介绍

Shell是一个命令行解释器,它为用户提供了一个向Linux内核发送请求以便运行程序的界面系统级程序。

用户可以用Shell来启动,挂起,停止,编写一些程序。

二,Shell脚本执行方式

2.1 格式要求

  1. 脚本以 #!/bin/bash 开头
  2. 脚本需要有可执行权限
  3. 一般shell脚本文件以 .sh 为后缀
  4. 在shell中用 # 表示注释
  5. 多行注释为 起始处 :<<! 结尾处 !

创建的shell脚本文件即使是root权限也不会给x执行权限,需要自己增加

2.2 常用执行方式

运行方式

  • 直接执行(./script.sh
  • 通过解释器运行(sh script.sh)。

三,变量

在 Shell 脚本中,变量是存储数据的核心工具,可分为 环境变量局部变量特殊变量数组变量 等类型。

3.1 变量分类与作用域

类型 作用域 生命周期 定义方式
局部变量 当前 Shell 进程 脚本或函数执行期间 var=value
环境变量 当前及子进程 直到 Shell 会话结束 export var=valuevar=value; export var
特殊变量 全局 脚本执行期间 预定义(如 $0, $#, $? 等)
只读变量 不可修改 定义后不可更改 readonly var=value
数组变量 当前 Shell 进程 脚本或函数执行期间 array=(val1 val2)

3.2 变量的定义与使用

基本语法

  • 赋值变量名=值等号两侧不能有空格

  • 引用$变量名${变量名}(推荐后者,明确变量边界)

  • 删除unset 变量名

  • 示例

    bash 复制代码
    name="Alice"         # 定义局部变量
    echo "Hello, $name"  # 输出:Hello, Alice
    echo "Hello, ${name}" # 更安全的写法
    unset name           # 删除变量

环境变量

  • 作用:传递到子进程(如脚本调用的其他程序)。

  • 定义方式

    bash 复制代码
    export PATH="/usr/bin:$PATH"  # 直接导出
    # 或先赋值再导出
    JAVA_HOME="/opt/java"
    export JAVA_HOME

只读变量

  • 特性:定义后不可修改或删除。

    bash 复制代码
    readonly PI=3.14
    PI=3.1415  # 报错:PI: readonly variable

特殊变量

  • Shell 预定义的特殊变量,用于获取脚本运行时的上下文信息:

    变量 含义
    $0 当前脚本的文件名
    $1, $2, ... 脚本的参数($1 是第一个参数,以此类推),但是如果是10就需要${10}
    $# 传递给脚本的参数个数
    $@ 所有参数列表(每个参数独立,如 "$@" 会保留参数中的空格)
    $* 所有参数合并为一个字符串(如 "$*" 相当于 "$1 $2 ..."
    $? 上一条命令的退出状态(0 表示成功,非 0 表示失败)
    $$ 当前 Shell 进程的 PID
    $! 最后一个后台运行进程的 PID
  • 示例

    bash 复制代码
    #!/bin/bash
    echo "脚本名:$0"         # 输出脚本文件名
    echo "第一个参数:$1"     # 输出传入的第一个参数
    echo "参数个数:$#"       # 输出参数总数
    echo "所有参数:$@"       # 输出参数列表(推荐使用)
    echo "上一条命令状态:$?" # 检查命令是否成功

数组变量

  1. 定义与访问

    bash 复制代码
    # 定义数组
    fruits=("apple" "banana" "cherry")  # 索引数组
    declare -A user=([name]="Alice" [age]=30)  # 一个Map数组
    
    # 访问元素
    echo ${fruits[0]}    # 输出:apple(索引从0开始)
    echo ${user["name"]} # 输出:Alice
    
    #获取所有元素
    echo ${fruits[@]}    # 输出所有元素
    echo ${!user[@]}     # 输出所有键(关联数组)
  2. 修改数组

    bash 复制代码
    fruits[1]="blueberry"  # 修改第二个元素
    fruits+=("orange")     # 追加元素
    unset fruits[2]        # 删除第三个元素

3.3 变量替换与字符串操作

变量替换:

语法 说明
${var:-default} var 未定义,返回 default
${var:=default} var 未定义,赋值为 default 并返回
${var:+replacement} var 已定义,返回 replacement
${var:?error_msg} var 未定义,输出错误信息并终止脚本

示例:

bash 复制代码
echo ${name:-"Guest"}  # 若name未定义,输出Guest

字符串操作

语法 说明
${#string} 获取字符串长度
${string:start:length} 截取子字符串(从 start 开始,长度 length
${string#pattern} 删除最短匹配前缀
${string##pattern} 删除最长匹配前缀
${string%pattern} 删除最短匹配后缀
${string%%pattern} 删除最长匹配后缀
${string/old/new} 替换第一个匹配的 oldnew
${string//old/new} 替换所有匹配的 oldnew

示例

bash 复制代码
path="/home/user/file.txt"
echo ${path#*/}      # 输出:home/user/file.txt(删除第一个/及之前内容)
echo ${path##*/}     # 输出:file.txt(删除最后一个/及之前内容)
echo ${path%.*}      # 输出:/home/user/file(删除最后一个.及之后内容)
echo ${path/file/doc} # 输出:/home/user/doc.txt

3.4 变量的引号使用

引号类型 作用
无引号 变量值中的空格会触发单词分割,通配符(*)会被扩展
双引号 保留变量值中的空格和特殊字符($, \, ! 等),变量会被替换
单引号 完全原样输出,变量和特殊字符不会被替换

示例

bash 复制代码
name="Alice Bob"
echo $name        # 输出:Alice Bob(正确,但若包含特殊字符可能出错)
echo "$name"      # 推荐写法,避免空格分割
echo '$name'      # 输出:$name(变量不会被替换)

3.5 注意事项

  1. 命名规则
    • 变量名由字母、数字、下划线组成,不能以数字开头。
    • 避免使用 Shell 关键字(如 if, then)。
  2. 空格问题
    • 赋值时等号两侧不能有空格:var=value(正确),var = value(错误)。
  3. 引号必要性
    • 处理含空格的字符串时,必须用双引号包裹变量:"$var"
  4. 作用域陷阱
    • 函数内未使用 local 定义的变量会污染全局作用域。

四,运算符

4.1 算术运算符

用于数值计算(需用 $(( ))expr)。

运算符 说明 示例
+ 加法 echo $((5 + 3)) → 输出 8
- 减法 echo $((10 - 2)) → 输出 8
* 乘法 echo $((4 * 2)) → 输出 8
/ 除法 echo $((16 / 2)) → 输出 8
% 取模(取余) echo $((9 % 2)) → 输出 1
** 幂运算 echo $((2 ** 3)) → 输出 8

示例脚本

bash 复制代码
a=10
b=3
sum=$((a + b)) # sum=$(expr $a + $b)  ,不建议用expr因为expr中*需要用\*进行转义,而且变量间需要空格
echo "和为:$sum"  # 输出:和为:13

4.2 比较运算符

用于数值或字符串的条件判断(需在 [ ][[ ]] 中使用)。

运算符 说明 示例
-eq 等于 [ $a -eq $b ]
-ne 不等于 [ $a -ne $b ]
-gt 大于 [ $a -gt $b ]
-lt 小于 [ $a -lt $b ]
-ge 大于等于 [ $a -ge $b ]
-le 小于等于 [ $a -le $b ]

示例脚本

bash 复制代码
if [ $a -gt 5 ]; then
  echo "a 大于 5"
fi

字符串比较

运算符 说明 示例
= 等于 [ "$str1" = "$str2" ]
!= 不等于 [ "$str1" != "$str2" ]
-z 空字符串 [ -z "$str" ]
-n 非空字符串 [ -n "$str" ]

示例脚本

bash 复制代码
str1="hello"
str2="world"
if [ "$str1" != "$str2" ]; then
  echo "字符串不同"
fi

4.3 逻辑运算符

用于组合多个条件。

运算符 说明 示例
&& 逻辑与 [ 条件1 ] && [ 条件2 ]
` ` 逻辑或 ` 条件1 条件2 `
! 逻辑非 [ ! 条件 ]

示例

bash 复制代码
if [ $a -gt 5 ] && [ $a -lt 15 ]; then
  echo "a 在 5 和 15 之间"
fi

4.4 字符串操作符

专用于字符串操作。

运算符 说明 示例
${#s} 获取字符串长度 echo ${#str} → 输出长度
${s:pos} 截取子字符串(从位置 pos 开始) echo ${str:2} → 从第3字符开始
${s:pos:len} 截取子字符串(从 pos 开始,长度 len) echo ${str:1:3} → 截取3字符

示例

bash 复制代码
str="abcdef"
echo "${str:2}"      # 输出:cdef
echo "${str:1:3}"    # 输出:bcd

4.5 文件测试运算符

用于检查文件/目录属性。

运算符 说明 示例
-e 文件/目录是否存在 [ -e "/path/file" ]
-f 是否为普通文件 [ -f "/path/file" ]
-d 是否为目录 [ -d "/path/dir" ]
-r 是否可读 [ -r "/path/file" ]
-w 是否可写 [ -w "/path/file" ]
-x 是否可执行 [ -x "/path/file" ]
-s 文件大小是否大于0字节 [ -s "/path/file" ]

示例

bash 复制代码
file="/tmp/test.txt"
if [ -f "$file" ]; then
  echo "$file 是普通文件"
fi

4.6 赋值运算符

用于变量赋值和运算简化。

运算符 说明 示例(假设初始值 a=5
= 赋值 a=10 → a 变为 10
+= 追加字符串 str="hi"; str+=" there" → "hi there"
+= 数值累加 ((a += 3)) → a 变为 8

示例

bash 复制代码
num=5
((num *= 2))  # num 变为 10
echo $num

五,条件判断

5.1 单分支

bash 复制代码
if [ 判断条件 ]
then
	条件为true执行的语句
fi

5.2 双分支

shell 复制代码
if [ 判断条件 ]
then
	条件为true执行的语句
else
	条件为flase执行的语句
fi

简写:

shell 复制代码
[ 判断条件 ] && 条件为true执行语句

注意点:

  1. 判断条件前后要有空格
  2. if 和条件之间需要空格
  3. [ ]返回false(有空格!)
  4. [ wwadwa ]返回true
  5. 0为true ,>1 为false

5.3 多分支

bash 复制代码
if [ 条件判断 ]
then
	代码
elif [ 条件判断 ]
then
	代码
...
fi

5.4 流程控制(case)

基本语法:

bash 复制代码
case $变量名 in
"值1")
	代码1
;;
"值2")
	代码2
;;
.....
*)
	最终情况
;;
esac

5.5 常用判断条件

在前面加上 ! 即为否定,比如 ! -f 文件不存在

字符串之间比较

shell 复制代码
if [ 字符串1 = 字符串2 ]

两个整数的比较

shell 复制代码
if [ 1 -lt 2 ]
符号 描述
-lt 小于
-le 小于等于
-eq 等于
-gt 大于
-ge 大于等于
-ne 不等于

按照文件权限判断

shell 复制代码
if [ 选项 文件路径 ]
符号 描述
-r 有读的权限
-w 有写的权限
-x 有执行的权限

按照文件类型进行判断

shell 复制代码
if [ 符号 文件路径 ]
符号 描述
-f 存在文件
-e 存在路径
-d 存在目录

示例:

bash 复制代码
if [ $1 -gt 100 ]; 
then
  echo "参数大于100"
elif [ -f "/path/to/file" ]; 
then
  echo "文件存在"
else
  echo "其他情况"
fi

六,循环

6.1 for循环

基本语法:遍历列表或范围

bash 复制代码
for 变量 in 列表; 
do
  命令
done

典型应用

  • 遍历数字范围

    bash 复制代码
    for i in {1..5}; 
    do
      echo "数字:$i"
    done
    # 输出:数字1到5
  • 遍历文件/目录

    bash 复制代码
    for file in *.txt; 
    do
      echo "处理文件:$file"
    done
  • 遍历数组

    bash 复制代码
    fruits=("apple" "banana" "cherry")
    for fruit in "${fruits[@]}"; 
    do
      echo "水果:$fruit"
    done
  • C 风格循环(仅限 Bash):

    bash 复制代码
    for ((i=0; i<5; i++));
    do
      echo "计数器:$i"
    done

6.2 while循环

基本语法:

bash 复制代码
while [ 条件 ]; 
do
  命令
done

典型应用

  • 读取文件内容

    bash 复制代码
    while read line; 
    do
      echo "行内容:$line"
    done < file.txt
  • 计数器控制

    bash 复制代码
    count=0
    while [ $count -lt 5 ]; 
    do
      echo "计数:$count"
      ((count++))
    done
  • 无限循环

    bash 复制代码
    while true; 
    do
      echo "按 Ctrl+C 终止"
      sleep 1
    done

6.3 until 循环

基本语法 :条件为假时持续执行(与 while 逻辑相反):

bash 复制代码
until [ 条件 ]; 
do
  命令
done

典型应用

  • 等待条件满足

    bash 复制代码
    until ping -c1 example.com &>/dev/null; 
    do
      echo "等待网络连接..."
      sleep 1
    done
    echo "连接成功!"

    &>/dev/null

    • 作用 :将命令的 所有输出(包括标准输出和错误输出)重定向到黑洞 (即不显示任何信息)。
      • >:重定向。
      • /dev/null:Linux 中的虚拟设备,丢弃所有写入的数据。
      • &>:同时重定向标准输出(stdout)和错误输出(stderr)。
    • 效果:让命令静默执行,不在终端显示结果。

6.4 循环控制语句

关键字 作用 示例
break 立即退出循环 if [条件]; then break; fi
continue 跳过当前迭代,进入下一次循环 if [条件]; then continue; fi
exit 终止整个脚本 if [错误]; then exit 1; fi

示例

bash 复制代码
for i in {1..10}; 
do
  if [ $i -eq 5 ]; 
  then
    break     # 当i=5时退出循环
  fi
  echo "当前值:$i"
done
# 输出:1 2 3 4

七,读取控制台输入内容

基本语法

bash 复制代码
read (选项) (参数) 接受值得变量

选项

  • -p 指定读取值时的提示信息
  • -t 指定读取值的等待时间,如果未在规定时间输入就不等待

举例

shell 复制代码
read -p "请输入一个值num=" NUM

其中NUM1是接受从控制台输入值的变量

-p 后面的内容是从控制台等待读取值的时候的提示信息

这个相当于scanf("请输入一个值num=%d",$NUM1)

shell 复制代码
read -t 10 NUM2

等待10s如果10后没有获取到数值就停止等待

结合起来写法

shell 复制代码
read -p 参数 -t 参数 接受值的变量

八,函数

在 Shell 脚本中,函数(Function)是将一组命令封装为可重复调用的代码块,用于提高代码复用性和逻辑模块化。以下是 Shell 函数的完整指南:


8.1 函数定义与调用

定义语法

bash 复制代码
# 方式1:标准定义
function_name() {
  命令
}

# 方式2(仅限 Bash):使用 `function` 关键字
function function_name {
  命令
}

调用函数

直接通过函数名调用,无需括号

bash 复制代码
function_name

示例

bash 复制代码
# 定义函数
say_hello() {
  echo "Hello, $1!"
}

# 调用函数并传参
say_hello "Alice"  # 输出:Hello, Alice!

8.2 函数参数

传递参数

函数内通过 $1, $2, ..., $n 接收参数:

bash 复制代码
sum() {
  result=$(( $1 + $2 ))
  echo "和为:$result"
}

sum 5 3  # 输出:和为:8

特殊参数

变量 含义
$# 函数接收的参数个数
$@ 所有参数的列表(数组)
$* 所有参数合并为字符串

示例

bash 复制代码
show_args() {
  echo "参数个数:$#"
  echo "参数列表:$@"
}

show_args "A" "B" "C"
# 输出:
# 参数个数:3
# 参数列表:A B C

8.3 返回值与退出状态

return 语句

  • 函数默认返回最后一条命令的退出状态码(0 表示成功,非 0 表示失败)。
  • 使用 return 显式返回状态码(范围:0~255)。
bash 复制代码
check_file() {
  if [ -f "$1" ]; then
    return 0  # 文件存在 → 成功
  else
    return 1  # 文件不存在 → 失败
  fi
}

check_file "test.txt"
echo $?  # 输出返回值(0 或 1)

返回数据

  • 通过变量传递:使用全局变量存储结果。
  • 通过 echo 输出 :结合命令替换($(...))捕获输出。
bash 复制代码
# 方式1:全局变量
result=""
calculate() {
  result=$(( $1 * 2 ))
}
calculate 5
echo $result  # 输出:10

# 方式2:echo 输出
double() {
  echo $(( $1 * 2 ))
}
value=$(double 5)
echo $value  # 输出:10

8.4 变量作用域

  • 默认全局变量:函数内外定义的变量均为全局变量。
  • 局部变量 :使用 local 关键字定义仅在函数内有效的变量。
bash 复制代码
demo_scope() {
  local local_var="内部变量"
  global_var="全局变量"
}

demo_scope
echo $global_var  # 输出:全局变量
echo $local_var   # 输出:空(变量不存在)

8.5 递归函数

函数可以调用自身,但需注意终止条件以避免无限递归。

bash 复制代码
factorial() {
  if [ $1 -le 1 ]; then
    echo 1
  else
    local prev=$(factorial $(( $1 - 1 )))
    echo $(( $1 * prev ))
  fi
}

echo "5的阶乘:$(factorial 5)"  # 输出:120

8.6 函数库与导入

将常用函数封装为独立文件(如 utils.sh),通过 source 导入使用:

bash 复制代码
# utils.sh
log() {
  echo "[$(date)] $1"
}

# main.sh
source utils.sh
log "任务开始"  # 输出带时间戳的日志

8.7 实用技巧

函数别名

使用 alias 为常用函数创建快捷命令:

bash 复制代码
# 定义函数
docker_clean() {
  docker system prune -af
}

# 设置别名(可选)
alias dclean="docker_clean"

调试函数

  • 显示执行过程

    bash 复制代码
    set -x  # 开启调试
    my_function
    set +x  # 关闭调试
  • 逐行调试

    bash 复制代码
    bash -x script.sh

错误处理

使用 trap 捕获错误信号:

bash 复制代码
handle_error() {
  echo "错误发生在第 $LINENO 行"
  exit 1
}

trap 'handle_error' ERR

8.8 实际应用示例

文件备份函数

bash 复制代码
backup_file() {
  local src="$1"
  local dest="${src}.bak_$(date +%Y%m%d)"
  if cp "$src" "$dest"; then
    echo "备份成功:$dest"
  else
    echo "备份失败!" >&2
    return 1
  fi
}

backup_file "data.txt"

用户输入验证

bash 复制代码
get_input() {
  read -p "请输入用户名(长度≥3):" username
  if [ ${#username} -lt 3 ]; then
    echo "用户名太短!" >&2
    return 1
  fi
  echo "$username"
}

valid_name=$(get_input) || exit 1

8.9 注意事项

  1. 避免函数名冲突:函数名不要与系统命令重复。
  2. 参数校验 :在函数内检查参数合法性(如 if [ $# -lt 1 ]; then ...)。
  3. 性能优化:避免在循环中频繁调用复杂函数。
相关推荐
groundhappy3 小时前
idalib安装和codex ida-mcp配置
linux·开发语言·python
通信小小昕3 小时前
Ubuntu 26.04 中文输入法安装
linux·运维·ubuntu
张小姐的猫4 小时前
【Linux】网络编程 —— HTTP协议(上)
linux·运维·服务器·网络·http·单例模式·策略模式
栩栩云生5 小时前
AI 写代码犯的错,早被写进了错题集
linux·安全·ai编程
imc.116 小时前
linux基础IO
linux·运维·服务器
BelongPanda8 小时前
Linux Nginx 纯手动 Let‘s Encrypt 泛域名证书配置教程
linux·nginx
酷可达拉斯8 小时前
Linux操作系统-shell编程(0)
linux·运维·服务器·python·云计算
2301_777998348 小时前
Linux中断机制:操作系统如何高效运行
linux·运维·服务器
yunwei378 小时前
eBPF 教程:检查 exec 后真正安装的可执行镜像
linux·开源
笑锝没心没肺9 小时前
yum update 报错:GPG key retrieval failed: [Errno 14] HTTPS Error 404 - Not Found
linux·运维·服务器