十一,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. 性能优化:避免在循环中频繁调用复杂函数。
相关推荐
yangang1852 小时前
linuxbash原理
linux·运维·服务器
小小毛桃2 小时前
在Ubuntu系统中运行Windows程序
linux·windows·ubuntu
码农新猿类2 小时前
服务器本地搭建
linux·网络·c++
小度爱学习2 小时前
linux中的执行命令格式及命令帮助
linux·运维·chrome
良许Linux3 小时前
嵌入式算吃青春饭么?
linux
良许Linux3 小时前
马上要毕业去工作了,做嵌入式软件开发工程师,但是完全不会编程怎么办?
linux
良许Linux3 小时前
学stm32,有什么学习方法?
linux
良许Linux3 小时前
为啥有好多人说 Arduino 是玩具?
linux
独行soc3 小时前
2025年常见渗透测试面试题-红队面试宝典下(题目+回答)
linux·运维·服务器·前端·面试·职场和发展·csrf
mosaicwang3 小时前
dnf install openssl失败的原因和解决办法
linux·运维·开发语言·python