当前位置: 首页 > news >正文

Linux之shell脚本

该篇文章主要介绍shell是什么、shell脚本的基本语法以及一些实例展示,带我们快速入门shell脚本编程。

shell与shell脚本

Shell 是操作系统中用户与内核之间的桥梁,它是一种“命令行解释器”(Command Line Interpreter),可以接收用户输入的命令并将其传递给操作系统执行。我们通常所说的 “Shell 编程” 或 “Shell 脚本”,就是用这种命令行语言编写的程序。所以我们要区分两种概念,shell本身是一个"命令行解释器",是用户输入的命令与操作系统之间交互的桥梁;而shell脚本就是一组写在文件里的命令,本质上shell脚本就是把多个命令写在一个文件中,让shell解释器一次执行完。

shell在类Unix系统中扮演的角色

在类 Unix 系统中,系统结构大致如下:

+----------------------------+
|        用户 (User)         |
+-------------+--------------+|v
+-------------+--------------+
|        Shell(命令行)     |  ⇦⇨ 用户输入命令 & 获取输出
+-------------+--------------+|v
+-------------+--------------+
|     系统调用接口(API)     |  ⇦⇨ Shell 通过系统调用与内核沟通
+-------------+--------------+|v
+-------------+--------------+
|      操作系统内核(Kernel)|   ⇦⇨ 管理硬件资源、调度程序等
+-------------+--------------+|v
+-------------+--------------+
|      硬件(CPU、内存等)    |
+----------------------------+

假设我们在命令行中输入了 ls -l:

用户输入命令:
+-------------------+
|   User Terminal   |
|   输入:ls -l     |
+---------+---------+|vShell 解释命令:
+---------+---------+
|     Shell (bash)  |
|  解析为 exec('ls')|
+---------+---------+|v通过系统调用执行程序:
+---------+---------+
|系统调用接口 (syscall) 
+---------+---------+|v操作系统调度运行 ls:
+---------+---------+
|     Kernel(内核)|
+---------+---------+|v调用磁盘、文件系统等
+---------+---------+
|    硬件/文件系统   |
+-------------------+

Shell 的基本作用

  • 解析命令:用户输入的命令由 Shell 解释并执行。

  • 提供交互式界面:用户可以输入命令、查看输出、进行交互操作。

  • 脚本执行:Shell 可以将一系列命令写入文件中,以脚本形式批量执行。

  • 控制程序流程:支持变量、条件判断、循环等控制结构。

shell脚本的创建

shell脚本文件的后缀为.sh

我们可以通过vim编辑器直接创建脚本文件或使用touch创建脚本文件

vim myscript.sh
touch myscript.sh

在创建好.sh文件后,在该文件的第一行一般我们要指定下shell解释器(注意这不是必须的,但推荐在sh文件的第一行指定下具体的shell解释器,否则直接运行./xxx.sh时会根据系统默认的解释器如/bin/sh来执行脚本,如果你的.sh是基于bash解释器编写的那么可能会导致不兼容或报错)。

#指定解释器为sh
#!/bin/sh#指定解释器为bash
#!/bin/bash.....

常见的shell解释器

shell解释器是一种统称,具体又会有不同的解释器,常见的shell解释器有sh、bash、zsh、dash等

解释器名称路径特点简述
sh/bin/sh最基础的 Unix Shell,兼容性强,功能有限(POSIX 标准)
bash/bin/bashLinux 默认 Shell,功能强大,支持数组、[[ ]] 条件表达式等
zsh/bin/zsh更强大、可配置、语法更灵活,配合 oh-my-zsh 很流行
dash/bin/dashUbuntu 中 /bin/sh 的默认指向,轻量、执行快,但语法限制多

shell脚本的执行

shell脚本的执行方式有多种:
方式一:

首先为shell脚本加权限

chmod +x myscript.sh

直接运行该脚本

./myscript.sh

注意:如果创建脚本时我们在第一行制定了具体的解释器,那此时执行该脚本时会使用制定好的解释器,否则使用默认shell解释器sh 

方式二:

显示指定解释器执行

#指定用sh解释器
sh myscript.sh#指定用bash解释器
bash myscript.sh

方式三:

在子shell中执行脚本内容(仍在当前shell环境中执行脚本,不是在新进程中),常用于设置环境变量、加载配置等

source myscript.sh  
或
. source myscript.sh

注意:source和.是shell命令,用来在当前shell环境中执行脚本内容,这意味着脚本中的所有命令都会在当前的shell环境中执行,而不是启动一个新进程。如果我们用./myscript.sh执行脚本,系统会启动一个新的shell进程来执行脚本,脚本中的环境变量或设置(如定义的变量、函数)会只在这个进程中有效,执行完后就消失了。但是,如果你用 source myscript.sh. myscript.sh 执行脚本,脚本中的内容会在当前的 Shell 环境中执行,这样脚本里设置的环境变量、函数等都能在执行完脚本后继续存在

示例:

myscript.sh

#!/bin/bash
export MY_VAR="hello"

如果用./执行后再打印MY_VAR

$ ./myscript.sh
$ echo $MY_VAR

输出:

(没有任何输出)

但如果使用source再打印

$ source myscript.sh
$ echo $MY_VAR

输出:

hello

shell脚本中的变量

1.系统环境变量

系统环境变量是在操作系统启动或用户登录时自动定义的变量,用于保存系统配置、用户信息、路径设置等。

变量名含义
$HOME当前用户的主目录路径(如 /home/username
$USER当前登录的用户名(如 root, john
$PATH系统查找可执行命令的路径列表(冒号 : 分隔)
$SHELL当前默认使用的 Shell 解释器路径(如 /bin/bash
$PWD当前工作目录的完整路径(print working directory)
$LANG当前语言/区域设置(如 en_US.UTF-8
echo "你的用户名是:$USER"
echo "你的家目录是:$HOME"
echo "当前目录是:$PWD"
echo "Shell 程序是:$SHELL"
echo "命令搜索路径是:$PATH"

2.shell特殊变量

这些变量在脚本执行过程中自动生效,不需要定义,直接使用

特殊变量说明
$0当前脚本的文件名
$1~$9传入脚本的第 1~9 个参数
$#传入参数的个数
$@以“参数列表”的方式展开所有参数(保留参数引用)
$*以“一个整体”的方式展开所有参数(不保留引用)
$?上一条命令的退出状态码(0 表示成功)
$$当前脚本运行的进程 ID
$!最后一个后台运行的进程的 PID

 myscript.sh

#!/bin/bash
echo "脚本名是:$0"
echo "第一个参数是:$1"
echo "第二个参数是:$2"
echo "参数个数是:$#"
echo "所有参数(\$@):$@"
echo "所有参数(\$*):$*"
echo "当前进程 PID:$$"

执行脚本

chmod +x test.sh
./test.sh apple banana

输出

脚本名是:./test.sh
第一个参数是:apple
第二个参数是:banana
参数个数是:2
所有参数($@):apple banana
所有参数($*):apple banana
当前进程 PID:3921

3.用户自定义变量

name="Tom"
age=25

注意:等号两边不能有空格,否则会被解释成命令;变量名建议只使用字母、数字、下划线,不能以数字开头。

使用变量:

echo "姓名是:$name"
echo "年龄是:$age"

补充:对于${变量}来说,$的作用是引用变量的值,即用来访问变量的值,建议用{}包起变量名后再用$取值,为了防止歧义。$运算符还有多种作用,后续会统一介绍。

变量的基本操作:

操作示例说明
赋值x=10定义变量
取值echo $x读取变量
删除变量unset x删除变量
只读变量readonly x定义只读变量,不能再修改

示例:

#!/bin/bashmyname="Alice"
readonly myname     # 冻结变量
echo "Hello, $myname"unset myname        # 会报错:不能删除只读变量

shell脚本基本语法结构

1.特殊符号

$

$ 符号用于 引用变量获取命令的输出指定参数执行进程替换 等多种用途。它是一个多用途的符号,非常常见且重要。

①引用变量的值

my_var="Hello, World!"
echo $my_var输出:Hello, World!

②获取命令的输出

current_date=$(date)
echo "今天的日期是: $current_date"输出:今天的日期是: Tue May  1 10:00:00 CST 2025

③获取位置参数

在 Shell 脚本中,$ 用于引用 脚本的参数。脚本在执行时可以接收外部传入的参数,这些参数通常通过 $1, $2, ..., $n 来引用,其中 1 表示第一个参数,2 表示第二个参数,以此类推。

# 脚本执行时传入参数
# ./script.sh arg1 arg2 arg3echo "第一个参数: $1"
echo "第二个参数: $2"
echo "第三个参数: $3"第一个参数: arg1
第二个参数: arg2
第三个参数: arg3

④获取所有位置参数

当你想要获取 所有参数 时,可以使用 $@$*。它们都表示所有的命令行参数,但有所区别:

  • $@:将每个参数视为单独的字符串,适用于循环等操作;

  • $*:将所有参数视为一个整体的字符串。

# 脚本执行时传入参数
# ./script.sh arg1 arg2 arg3echo "所有参数 (\$@): $@"
echo "所有参数 (\$*): $*"输出:
所有参数 ($@): arg1 arg2 arg3
所有参数 ($*): arg1 arg2 arg3

⑤获取脚本执行状态(退出状态)

$? 用于获取上一条命令的退出状态。每个命令在执行时都会返回一个退出状态(exit status),通常返回 0 表示成功,非 0 表示失败。

mkdir new_dir
echo "上一条命令的退出状态: $?"上一条命令的退出状态: 0

⑥获取后台进程PID

$!用于记录最近一次后台进程的PID

&

①后台运行命令,不阻塞当前shell

#在后台运行command
command &sleep 10 &  # 后台休眠10秒,立即返回提示符,返回格式:[作业号] PID(如 [1] 12345)

②在子shell中并发执行多条命令

{ command1 & command2 & }  { sleep 3; echo "A"; } & { sleep 1; echo "B"; } &输出(可能顺序):
B  # 先完成的任务先输出
A

③重定向中的特殊用途

command > file.txt 2>&1  #将stderr重定向到stdout输出的位置,即file.txt中2>&1 表示将文件描述符 2(stderr)指向描述符 1(stdout)的当前位置。
此时执行command产生的stdout和stderr都被重定向写入到了file.txt中

④逻辑操作符与运算操作符

[[ -f file.txt ]] & echo "检查文件"  # 异步检查文件是否存在echo $(( 5 & 3 ))  # 输出 1(二进制 101 & 011 = 001)

注释#

Shell 中注释以 # 开头,整行或行尾注释,Shell 执行时会忽略这些内容。

# 这是一个注释
name="Alice"  # 设置用户名变量

换行\

每条命令默认使用换行分隔

echo "第一行"
echo "第二行"输出结果:
第一行
第二行

也可以使用 \ 实现 命令换行(续行:该行未结束,下一行是当前行的继续):

echo "这是一条非常 \
长的命令"输出结果:
这是一条非常 长的命令

转义\

当我们需要在字符串中输出一些特殊字符(例如空格、引号、美元符号等)时,可以使用 \ 来转义它们。

echo "He said, \"Hello!\""

输出:

He said, "Hello!"

分号;

使用分号可以在同一行中执行多个命令

echo "hello"; echo "world"

等价于:

echo "hello"
echo "world"

&&  ||

&&:前一个命令成功(返回值为 0)时才会执行后一个命令

||:前一个命令失败(返回值非 0)时才会执行后一个命令

# 如果命令成功,则执行第二条命令
mkdir new_dir && cd new_dir# 如果命令失败,则执行第二条命令
mkdir new_dir || echo "目录创建失败"

2.输入输出

echo:输出内容到终端

echo "Hello, World!"echo -n "不换行输出"
echo -e "换行符:\n下一行"参数	      作用
-n	         不换行
-e	  启用转义字符(如 \n、\t)

read:从用户输入读取数据

read name
#用户再命令行中输入abc
echo "你好,$name"   #打印 你好,abc带提示信息:
read -p "请输入你的名字:" name
echo "你好,$name"多个变量:
read first last
echo "First: $first, Last: $last"

3.运算符

算术运算符

+$((3 + 2))
-$((5 - 1))
*$((2 * 3))
/$((10 / 2))
%取余$((7 % 4))

比较运算符(整数)

用于 test[ ]

-eq相等[ "$a" -eq "$b" ]
-ne不等[ "$a" -ne "$b" ]
-gt大于[ "$a" -gt "$b" ]
-lt小于[ "$a" -lt "$b" ]
-ge大于等于[ "$a" -ge "$b" ]
-le小于等于[ "$a" -le "$b" ]

 字符串比较运算符

===字符串相等[ "$a" = "$b" ]
!=字符串不等[ "$a" != "$b" ]
-z字符串长度为 0[ -z "$a" ]
-n字符串长度不为 0[ -n "$a" ]

 逻辑运算符

![ ! -f file.txt ]
-a[ -f a.txt -a -r a.txt ]
-o[ "$a" -lt 0 -o "$b" -gt 100 ]

4. 条件判断结构

[ ]:条件测试命令test的简写

[ 条件表达式 ]

         注意:"[" 与 "条件" 与 "]"都要用空格分开

[[ ]]:加强版的[ ]

        支持字符串比较

name="admin"# 传统写法
[ "$name" = "admin" ] && echo "匹配"# Bash 写法
[[ $name == "admin" ]] && echo "匹配"

         支持通配符

name="Alice"[[ $name == A* ]]&& echo "名字以 A 开头"
[[ $name == *ce ]] && echo "名字以 ce 结尾"

        支持正则匹配

email="user@example.com"=~:使用正则表达式进行匹配
不需要加引号(加了反而当普通字符串处理)[[ $email =~ ^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$ ]] && echo "这是合法邮箱"

        支持多条件判断(支持|| &&)

age=25if [[ $age -gt 18 && $age -lt 65 ]]; thenecho "在工作年龄范围内"
fi

        更好的鲁棒性:变量为空不报错

name=""# 传统写法可能出错
# [ $name = "admin" ]    # 报错:一元运算符预期# Bash 写法不会出错
[[ $name == "admin" ]]   # 安全 ✅

if-then

if [ condition ]; thencommands
fi

if-else

if  [ "$age" -ge 18 ]; thenecho "成年人"
elseecho "未成年人"
fi

if-elif-else

if [ "$score" -ge 90 ]; thenecho "优秀"
elif [ "$score" -ge 60 ]; thenecho "及格"
elseecho "不及格"
fi

test 命令

test $a -gt 5 && echo "a > 5"#等价于
[ $a -gt 5 ] && echo "a > 5"

shell脚本流程控制

1.if-else、elif语句

if 条件; then命令
elif 另一个条件; then命令
else命令
fi
#!/bin/bashread -p "请输入一个数字:" numif [[ $num -gt 100 ]]; thenecho "大于100"
elif [[ $num -eq 100 ]]; thenecho "等于100"
elseecho "小于100"
fi

2 case 语句

case $变量 in模式1)命令;;模式2)命令;;*)默认命令;;
esac
#!/bin/bashread -p "请输入操作(start/stop/restart):" actioncase $action instart)echo "服务已启动";;stop)echo "服务已停止";;restart)echo "服务已重启";;*)echo "未知命令";;
esac

3 for 循环

语法格式 1:遍历列表

for 变量 in 列表; do命令
done
for name in Alice Bob Charlie; doecho "你好,$name"
done

语法格式 2:数值循环(C 风格)

for ((i=1; i<=5; i++)); doecho "第 $i 次循环"
done

4 while 循环

while 条件; do命令
done
count=1
while [[ $count -le 3 ]]; doecho "循环第 $count 次"((count++))
done

也可用于读取文件逐行:

while read line; doecho "$line"
done < 文件名.txt

5 break 与 continue

for ((i=1; i<=5; i++)); doif [[ $i -eq 3 ]]; thencontinue  # 跳过第3次fiif [[ $i -gt 4 ]]; thenbreak     # 提前结束fiecho "第 $i 次"
done

shell脚本中的函数

1.函数的定义与调用

①函数的定义方式

# 方式一:推荐使用
function 函数名() {命令列表
}# 方式二:兼容写法
函数名() {命令列表
}

②函数的调用方式

调用方式非常简单,直接写函数名即可:

say_hello() {echo "Hello, Shell!"
}say_hello  # 调用无参函数say_hello() {echo "Hello $0!"
}say_hello bob # 调用有参函数

注意:shell脚本是逐行运行,不会像其它语言一样先编译,所以必须在函数调用前声明函数 

③函数体中的局部变量与全局变量

foo() {local name="Alice"    # 局部变量,仅在函数内有效echo "Hi, $name"
}name="Bob"
foo
echo "外部变量 name = $name"  # 不受 foo 中的修改影响

2.函数中的参数与返回值

①参数

Shell 函数接收参数的方式和脚本一样,用 $1$2$3 表示第 1、2、3 个参数:

greet() {echo "你好,$1!你今天感觉怎么样?"
}greet "小明"

输出:

你好,小明!你今天感觉怎么样?

②返回值

函数返回值方式一:用 echo

get_name() {echo "Shell脚本"
}
name=$(get_name)
echo "返回值是:$name"

函数返回值方式二:用 return 返回状态码(0~255)

is_even() {if (( $1 % 2 == 0 )); thenreturn 0elsereturn 1fi
}is_even 8
if [[ $? -eq 0 ]]; thenecho "是偶数"
elseecho "是奇数"
fi

shell脚本进阶

1 数组与字符串处理

①数组(仅bash支持)

定义数组

arr=(apple banana cherry)

访问数组元素

echo ${arr[0]}

获取所有元素

echo ${arr[@]}

获取数组长度

echo ${#arr[@]}

获取数组中某一元素的长度

echo ${#arr[0]}

遍历数组

for item in "${arr[@]}"; doecho "$item"
done

修改元素

arr[1]="orange"

②字符串处理

字符串长度

str="Hello Shell"
echo ${#str}   #输出11

子串提取

echo ${str:6:5}   #从下标6开始向后截取5个长度的元素

查找并替换

echo ${str/Shell/Bash}  #将str中的Shell替换为Bash,输出Hello Bash

字符串拼接

a="hello"
b="world"
c="$a $b"
echo $c

2 .输入输出重定向

①输出重定向

echo "写入文件" > file.txt     # 将"写入文件"覆盖写入到文件file.txt 
echo "追加内容" >> file.txt    # 将"追加内容"追加写入到文件file.txt 

②输出重定向

while read line; do    #每次读取file.txt中一行数据,重定向输出到控制台echo "$line"
done < file.txt

③多行输入

cat <<EOF > file.txt     #将多行数据一次性写入到file.txt中
这是多行内容
可以写很多行
EOF

④标准错误重定向

#执行ls一个不存在的文件会产生一个标准错误,
#可以通过 2> error.log 将这个标准错误重定向到error.log文件中
#2指的就是stderr的文件描述符
ls not_exist_file 2> error.log  

⑤将命令执行结果同时重定向给stdout和stderror

#将command的运行结果stdout重定向到out.log中
#将command运行产生的标准错误重定向到error.log中
command > output.log 2> error.log #将command的运行结果的标准输出和标准错误均重定向到all.log文件中
command > all.log 2>&1   #&1指的是获取文件描述符1即stdout的当前位置,即all.log
等价于
command > all.log 2> all.log#将ping这条指令的标准输出stdout和标准错误均重定向到空设备(黑洞设备),丢弃所有正常输出
ping -c 1 www.baidu.com > /dev/null 2>&1

3.错误处理与调试

①退出码检查 $?

cp file.txt /some/path/
if [ $? -ne 0 ]; thenecho "拷贝失败!"
fi

注意:$?存储的是最近一条指令执行的退出状态码

set -e:遇到错误立即退出脚本

#!/bin/bash
set -ecp file.txt /path/       # 若失败,则退出脚本
echo "这行不会执行"

set -x:打印执行过程(调试用)

自动打印 实际执行的每一行命令及其参数(展开变量后的真实命令)

set -x
echo "当前用户: $USER"#控制台输出
+ echo '当前用户: alice'  # 这是set -x的调试输出
当前用户: alice           # 这是echo的输出

4.使用 trap 捕捉信号

trap 是 Shell 脚本中用于 捕获并处理信号 的关键命令,它能让脚本在收到特定系统信号时执行自定义操作(比如清理资源、打印日志等),而不是直接终止。

在 Linux/Unix 系统中,信号是进程间通信的一种方式,用于通知进程发生了某些事件。常见信号:

  • INTSIGINTCtrl+C 触发)

  • TERMSIGTERM,默认的 kill 命令发送的信号)

  • KILLSIGKILL,强制终止,不可捕获)

  • HUPSIGHUP,终端挂断)

  • EXIT(非标准信号,脚本退出时触发)

trap 的基本语法

trap '执行的命令' 信号列表单信号:trap 'echo 收到信号' INT多信号:trap 'echo 退出' INT TERM EXIT忽略信号:trap '' INT(空字符串表示忽略)恢复默认:trap - INT(取消自定义处理)

示例一:优雅处理终端

#!/bin/bash
trap 'echo "捕获中断,正在退出..."; exit' INTwhile true; doecho "运行中..."sleep 1
done

示例二:脚本退出时清理资源

#!/bin/bash
tempfile="/tmp/mytemp.$$"  # 临时文件
trap 'rm -f "$tempfile"; echo "清理完成"' EXITtouch "$tempfile"
echo "脚本运行中..."
sleep 10

示例三:捕捉后执行函数

#!/bin/bash
cleanup() {echo "正在清理..."rm -f *.tmpexit
}
trap cleanup INT TERM# 模拟任务
touch {1..3}.tmp
sleep 60

shell脚本实战

示例一:批量重命名文件

在一个目录中,所有图片文件命名格式杂乱无章,比如:IMG001.jpgimg_02.JPGphoto3.jpeg,你希望将所有图片统一重命名为 image_001.jpgimage_002.jpg … 的格式,便于管理。

#!/bin/bashcount=1
# 遍历所有支持的图片格式(包括大小写变体)
for file in *.jpg *.jpeg *.JPG *.JPEG; do# 跳过无效匹配(当没有文件时会返回原始模式如"*.jpg")[ -f "$file" ] || continue# 生成新文件名(强制使用.jpg扩展名):#   %03d 表示3位数字(001, 002...)new_name=$(printf "image_%03d.jpg" "$count")# 执行重命名(原扩展名无论是什么,都改为.jpg)mv -- "$file" "$new_name"# 计数器增加((count++))
done

示例二:Git 仓库自动部署系统

企业服务端项目部署,要求:

  1. 监听某个 Git 仓库的更新;

  2. 自动拉取 main 分支最新代码;

  3. 构建项目并备份旧版;

#!/bin/bash# 基础配置
REPO_DIR="/var/www/myapp"   # 监控的Git仓库目录
SERVICE_NAME="myapp"        # 服务名称(需与systemd单元名一致)
LOG_FILE="/var/log/git_deploy.log"  # 日志文件路径# 进入Git仓库目录
cd "$REPO_DIR" || {echo "$(date) - ERROR: 无法进入项目目录" >> "$LOG_FILE"exit 1
}# 获取当前和远程的main分支差异
git fetch origin main >> "$LOG_FILE" 2>&1
LOCAL_COMMIT=$(git rev-parse main)
REMOTE_COMMIT=$(git rev-parse origin/main)# 如果没有更新则退出
[ "$LOCAL_COMMIT" = "$REMOTE_COMMIT" ] && exit 0# 记录部署开始
echo "$(date) - 检测到新提交: $REMOTE_COMMIT" >> "$LOG_FILE"# 备份当前可执行文件(假设编译产物在build目录)
[ -f build/myapp ] && cp build/myapp build/myapp.bak# 拉取最新代码
if ! git reset --hard origin/main >> "$LOG_FILE" 2>&1; thenecho "$(date) - ERROR: Git拉取失败" >> "$LOG_FILE"exit 2
fi# 构建项目(假设使用CMake)
{mkdir -p buildcd build || exit 1cmake .. && make -j$(nproc)
} >> "$LOG_FILE" 2>&1if [ $? -ne 0 ]; thenecho "$(date) - ERROR: 构建失败" >> "$LOG_FILE"# 尝试恢复备份[ -f build/myapp.bak ] && mv build/myapp.bak build/myappexit 3
fi

示例三:部署前自动检查依赖并构建 CMake + Make 项目

你维护一个使用 CMakemake 的 C++ 项目,部署前需要执行以下步骤:

  • 检查是否安装了 g++cmake

  • build/ 目录不存在,则创建并进入;

  • 执行 cmake ..make 构建;

  • 所有输出统一写入日志文件 deploy.log

#!/bin/bash# 定义日志文件路径(当前目录下的deploy.log)
LOG_FILE="./deploy.log"
# 定义构建目录路径(当前目录下的build文件夹)
BUILD_DIR="./build"# 在日志文件中记录部署开始时间(tee命令同时输出到终端和日志文件)
echo "----- DEPLOY STARTED: $(date) -----" >> "$LOG_FILE"# 1. 检查必要的编译工具是否安装# 检查cmake是否可用(command -v会返回命令路径,&>/dev/null丢弃所有输出)
if ! command -v cmake &>/dev/null; then# 如果cmake不存在,输出错误信息(tee -a表示追加到日志文件)echo "[ERROR] cmake is not installed!" | tee -a "$LOG_FILE"exit 1  # 退出状态码1表示常规错误
fi# 检查g++是否可用
if ! command -v g++ &>/dev/null; thenecho "[ERROR] g++ is not installed!" | tee -a "$LOG_FILE"exit 1
fi# 2. 准备构建目录# 检查构建目录是否存在(-d测试目录是否存在)
if [ ! -d "$BUILD_DIR" ]; thenecho "[INFO] Creating build directory..." | tee -a "$LOG_FILE"mkdir "$BUILD_DIR"  # 创建构建目录
fi# 进入构建目录,如果失败则报错(|| 表示前一个命令失败时执行后续命令)
# { } 命令组用于组合多个命令,必须用分号结尾
cd "$BUILD_DIR" || { echo "[ERROR] Cannot enter build directory!" | tee -a "$LOG_FILE"exit 1 
}# 3. 运行CMake配置项目echo "[INFO] Running cmake..." | tee -a "$LOG_FILE"
# 执行cmake并将标准输出和错误输出都重定向到日志文件(2>&1)
cmake .. >> "$LOG_FILE" 2>&1# 检查上一条命令的退出状态($?)
if [ $? -ne 0 ]; then  # -ne表示不等于0(非成功状态)echo "[ERROR] cmake failed!" | tee -a "$LOG_FILE"exit 2  # 使用不同的退出码便于识别错误阶段
fi# 4. 使用Make编译项目echo "[INFO] Building project with make..." | tee -a "$LOG_FILE"
# -j$(nproc) 使用所有CPU核心并行编译,nproc获取CPU数量
make -j$(nproc) >> "$LOG_FILE" 2>&1if [ $? -eq 0 ]; then  # -eq表示等于0(成功状态)echo "[SUCCESS] Build completed successfully!" | tee -a "$LOG_FILE"
elseecho "[ERROR] Build failed!" | tee -a "$LOG_FILE"exit 3  # 不同的退出码表示不同阶段的错误
fi# 在日志文件中记录部署结束时间
echo "----- DEPLOY ENDED: $(date) -----" >> "$LOG_FILE"

相关文章:

  • 多商户商城系统开发全策略:从技术架构到流量增长
  • 前端八股 3
  • 2025智能体的发展趋势
  • 分寝室(C++完成)
  • [UVM]寄存器模型的镜像值和期望值定义是什么?他们会保持一致吗?
  • Socket通信
  • SQL注入与简单实战
  • 动态规划简单题
  • Origin绘图操作:图中迷你图绘制
  • 欧拉计划 Project Euler62(立方数重排)题解
  • GESP2024年6月认证C++八级( 第一部分选择题(11-15))
  • 图像增强技术:从基础原理到企业级开发实战
  • NU1680低成本、无固件、高集成度无线充电电源接收器
  • 如何阅读GitHub上的深度学习项目
  • 【人工智能】图神经网络(GNN)的推理方法
  • 本地部署 n8n 中文版
  • 从 Python 基础到 Django 实战 —— 数据类型驱动的 Web 开发之旅
  • 【业务领域】计算机网络基础知识
  • gephi绘图
  • 开源革命:从技术共享到产业变革——卓伊凡的开源实践与思考-优雅草卓伊凡
  • 王毅谈金砖国家开展斡旋调解的经验和独特优势
  • 马上评|扩大高速免费救援范围,打消出行后顾之忧
  • 印度宣布即日起对所有巴基斯坦航班关闭领空
  • 网警查处编造传播“登顶泰山最高可得3万奖金”网络谣言者
  • 启程回家!神十九轨道舱与返回舱成功分离
  • 言短意长|新能源领军者密集捐赠母校