一、Shell 编程面试核心知识点梳理(详版・面试适配)
1.程序执行类型(解释型 vs 编译型)
核心定义
| 类型 | 执行原理 | 典型语言 / 文件 | 核心特点 | 面试高频问法 |
|---|---|---|---|---|
| 解释型 | 依赖解释器,边解析边执行,无需编译 | Shell(.sh)、Python | 开发快、跨平台,运行稍慢 | "Shell 脚本为什么需要 bash 解释器才能运行?" |
| 编译型 | 先编译为机器码可执行文件,直接运行 | C/C++(.c/.cpp) | 运行快,需适配不同系统 | "解释型和编译型语言的核心区别?" |
面试标准答案
解释型语言(如 Shell)无需提前编译,通过解释器逐行解析执行,开发效率高但运行效率较低;编译型语言需先编译为机器可识别的可执行文件,运行效率高但开发周期长。Shell 脚本属于解释型,必须指定
#!/usr/bin/bash声明解释器才能执行。
2.Shell 变量体系(3 类核心)
1. 本地变量
- 定义 :
var=value(等号无空格),仅当前 Shell / 脚本生效,子进程不可访问。 - 命名 :习惯小写(如
line=10),区分环境变量。 - 面试考点:"本地变量和环境变量的作用域区别?"→ 本地变量仅当前会话有效,脚本结束即销毁。
2. 环境变量
- 定义 :
export var=value导出,全局生效,子进程可继承。 - 常用示例 :
PATH(命令搜索路径)、HOME(用户家目录)、PS1(命令行提示符)、$$(当前进程号)。 - 命名:固定大写,系统默认 / 用户导出均可。
- 面试考点 :"如何让脚本中的变量被子脚本读取?"→ 用
export导出为环境变量。
3. 参数变量(位置参数)
- 定义:脚本运行时通过命令行传入的参数,系统自动赋值。
- 核心变量 :
$0:脚本名;$1-$9:第 1-9 个参数;${10}:第 10 个参数(需大括号);$#:参数总数;$*/$@:所有参数(前者整体,后者独立)。
- 面试考点 :"写脚本接收 2 个参数,计算两数之和"→ 基础写法:
echo $(( $1 + $2 ))。
3.if 条件判断(3 类核心场景)
通用语法(必考细节)
if [ 条件 ]; then # [ ] 前后必须加空格,; 可换行为then
命令
elif [ 条件 ]; then
命令
else
命令
fi
1. 字符串判断(处理文本 / 输入)
| 操作符 | 含义 | 示例 | 避坑点 |
|---|---|---|---|
= |
相等 | [ "$str" = "hello" ] |
变量必须套双引号,= 加空格 |
!= |
不相等 | [ "$str" != "exit" ] |
空变量不加引号会语法报错 |
-z |
为空 | [ -z "$str" ] |
判断用户是否未输入内容 |
-n |
非空 | [ -n "$str" ] |
验证配置项是否填写 |
2. 算术判断(处理数字比较)
| 操作符 | 含义 | 示例 | 避坑点 |
|---|---|---|---|
-eq |
等于 | [ $num -eq 10 ] |
禁用 ><(重定向符号) |
-ne |
不等于 | [ $num -ne 5 ] |
仅支持整数,小数需用 bc |
-gt |
大于 | [ $num -gt 60 ] |
如$score > 60会创建空文件 |
-ge |
大于等于 | [ $num -ge 18 ] |
- |
-lt |
小于 | [ $num -lt 100 ] |
- |
-le |
小于等于 | [ $num -le 50 ] |
- |
3. 文件测试(处理路径 / 权限)
| 操作符 | 含义 | 示例 | 面试高频场景 |
|---|---|---|---|
-e |
路径存在 | [ -e "$path" ] |
先判存在,再判类型 / 权限 |
-f |
普通文件 | [ -f "test.sh" ] |
判断是否为脚本 / 文本文件 |
-d |
目录 | [ -d "logs" ] |
判断备份目录是否存在 |
-r |
可读 | [ -r "$file" ] |
校验读取日志权限 |
-w |
可写 | [ -w "$file" ] |
校验写入配置权限 |
-x |
可执行 | [ -x "test.sh" ] |
校验脚本执行权限 |
面试高频题
问:"如何判断一个路径是文件且可执行?"答:
if [ -f "$path" ] && [ -x "$path" ]; then echo "可执行文件"; fi
4.循环结构(3 类核心循环)
1. for 循环(遍历 / 固定次数)
- 基础写法 :
for i in 1 2 3; do echo $i; done - 范围遍历 :
for i in {1..10}; do ...(生成 1-10) - 面试考点 :"遍历当前目录所有.sh 文件,添加执行权限"→
for file in *.sh; do chmod +x $file; done
2. while 循环(条件为真循环)
-
计数循环 :
i=0 while [ "$i" -lt 10 ]; do echo $i i=$((i+1)) # 现代写法,替代老式expr $i + 1 done -
无限循环 :
while true; do echo hello; sleep 1; done(等价while [ : ]; do ...) -
面试考点 :"写无限循环,输出当前时间,每 5 秒一次"→ 核心:
while true; do date; sleep 5; done
3. until 循环(条件为假循环,与 while 相反)
-
基础写法 :
i=0 until [ "$i" -ge 10 ]; do # i≥10时停止 echo $i i=$((i+1)) done -
面试考点:"while 和 until 的区别?"→ while 条件为真执行,until 条件为假执行。
5.面试高频避坑点(加分项)
- 变量引号:字符串 / 路径变量必须套双引号,防止空值 / 空格导致语法错误;
- 算术运算 :
$((i+1))效率高于expr,小数比较用bc(如echo "3.5>2.8" | bc); - 循环退出 :无限循环可通过
break(退出循环)、continue(跳过本次)、Ctrl+C终止; - 语法空格 :
[ ]/(( ))前后、操作符两边必须加空格,否则报错。
6.核心总结(面试速记)
- 执行类型:解释型(Shell)靠解释器,编译型(C)先编译后运行;
- 变量体系:本地(局部)、环境(全局)、参数(外部传入),export 导出环境变量;
- 条件判断:字符串(=/-z)、算术(-gt/-eq)、文件(-f/-d),[] 必加空格;
- 循环结构:for 遍历、while 条件循环(支持无限)、until 反向循环,优先用 $(()) 做算术。
5.case 语句:多分支匹配的高效写法
1. 核心作用
- 替代复杂的
if-elif-else多分支判断,让代码更简洁、易读。 - 适合处理固定选项匹配的场景(如用户输入选择、命令参数判断)。
2. 语法规则(面试必记)
case "$变量" in
模式1|模式2)
命令1
命令2
;; # 双分号表示当前分支结束
模式3)
命令3
;;
*) # 默认分支,匹配所有未命中的情况
默认命令
;;
esac # case 倒写作为结束标记
✅ 语法细节:
- 变量建议用双引号包裹(如
"$line"),防止空值或空格导致语法错误。 - 模式支持管道符
|表示 "或" 关系(如yes|y表示匹配yes或y)。 *)是默认分支,必须放在最后,匹配所有未被前面模式命中的情况。- 每个分支必须以
;;结尾,否则会报错。
3. 结合你的代码示例
while true
do
echo "input:"
read line
case "$line" in
yes|y ) echo "this is yes";;
no|n ) echo "this is no";;
end) break;; # 输入end时退出循环
*) echo "***";; # 输入其他内容时输出***
esac
done
- 运行逻辑 :无限循环等待用户输入,根据输入内容匹配不同分支:
- 输入
yes或y→ 输出this is yes - 输入
no或n→ 输出this is no - 输入
end→ 执行break退出整个循环 - 输入其他内容 → 输出
***
- 输入
4. 面试高频考点
- 问 :
case和if-elif-else有什么区别?→ 答 :case更适合处理固定值的多分支匹配,代码更简洁;if-elif-else适合复杂的条件判断(如范围、逻辑组合)。 - 问 :
case中的*)有什么作用?→ 答:是默认分支,当所有前面的模式都不匹配时执行,避免遗漏场景。 - 问 :为什么每个分支结尾要用
;;?→ 答 :;;是 Shell 规定的分支结束标记,用来分隔不同的模式分支,防止命令串到下一个分支。
6.Shell 函数:代码复用与模块化
1. 核心作用
- 将重复代码封装成函数,实现代码复用,提高脚本的可维护性。
- 让脚本逻辑更清晰,便于调试和扩展。
2. 语法规则(面试必记)
# 定义函数
函数名() {
# 函数体(可以使用参数变量 $1、$2...)
命令
return 返回值 # 可选,返回值范围0-255(默认0表示成功)
}
# 调用函数
函数名 参数1 参数2
✅ 语法细节:
- 函数定义时,
()内不需要写参数列表,参数通过$1、$2、$#等位置变量获取。 return用于返回函数执行状态(0-255),若需返回数值结果,建议用echo输出,再通过捕获输出获取(如result=$(函数名 参数))。- 函数内的
$#表示函数接收的参数个数,而非脚本的参数个数。
3. 结合你的代码示例
示例 1:my_add 加法函数
my_add() {
if [ "$#" -ne 2 ] # 判断参数个数是否为2
then
return 0 # 参数错误时返回0
fi
val=`expr $1 + $2` # 计算两个参数的和
return $val # 返回计算结果(注意:返回值不能超过255)
}
# 调用函数
my_add 2 3
echo "返回值=$?" # $? 是上一个命令的退出状态,这里获取函数的return值
⚠️ 注意 :Shell 函数的 return 值范围是 0-255,若计算结果超过 255 会溢出。如需返回任意数值,建议用 echo 输出:
my_add() {
if [ "$#" -ne 2 ]; then echo 0; return; fi
echo $(($1 + $2)) # 用echo输出结果
}
result=$(my_add 2 3) # 捕获函数输出
echo "结果=$result" # 输出:结果=5
示例 2:fun 函数(演示参数传递)
fun() {
echo "fun run"
echo "fun: argc=$#" # 函数参数个数
echo "fun: \$0=$0" # 脚本名(不是函数名)
echo "fun: \$1=$1" # 第一个函数参数
echo "fun: \$2=$2" # 第二个函数参数
}
# 调用函数
fun hello world
-
运行输出 :
plaintext
fun run fun: argc=2 fun: $0=脚本名.sh fun: $1=hello fun: $2=world
4. 面试高频考点
- 问 :Shell 函数如何传递参数?→ 答 :调用函数时直接跟参数,函数内部通过
$1、$2、$#等位置变量获取。 - 问 :Shell 函数的
return和echo有什么区别?→ 答 :return用于返回执行状态(0-255),echo用于返回具体结果(适合数值、字符串等)。 - 问 :为什么
my_add 2 3后直接echo $val为空?→ 答 :函数内的val是本地变量,函数执行结束后会销毁。如需获取函数内的变量,需用echo输出或全局变量(不推荐)。 - 问 :Shell 函数和 C 语言函数有什么区别?→ 答:Shell 函数是解释执行的,参数通过位置变量传递,返回值是状态码;C 语言函数是编译执行的,参数类型固定,返回值可以是任意类型。
三、复习速记清单(面试前快速过)
case 语句
- 语法:
case ... in ... esac,分支用;;结尾。 - 场景:多选项匹配,替代复杂
if-elif。 - 必记:
*)是默认分支,变量加双引号,模式支持|或。
Shell 函数
- 定义:
函数名() { ... },参数用$1、$2获取。 - 返回:
return传状态(0-255),echo传结果。 - 调用:
函数名 参数1 参数2,用$()捕获输出。
5.Shell 脚本执行与传参 面试核心速记
一、脚本执行的两种方式
1. 直接执行(./a.sh)
- 原理:启动一个新的 Shell 解释器子进程来执行脚本。
- 特点:脚本里定义的本地变量不会影响当前 Shell 环境,执行结束后子进程销毁,变量随之消失。
- 面试考法:问 "直接执行脚本和 source 脚本有什么区别?" → 答:直接执行会创建子进程,而 source 在当前进程执行。
2. 用 . 或 source 执行(. ./a.sh / source ./a.sh)
- 原理:在当前 Shell 解释器进程中直接执行脚本,不会创建子进程。
- 特点:脚本里的变量、函数等会直接加载到当前 Shell 环境,执行后依然可用。
- 典型场景 :加载配置文件(如
.bashrc),让配置立即生效。 - 面试考法 :问 "如何让脚本里的环境变量在当前 Shell 生效?" → 答:用
source或.执行脚本。
二、脚本传参的两种方式
1. 命令行参数传递
- 用法 :执行脚本时直接跟参数,如
./a.sh hello world。 - 获取方式 :脚本内部通过位置变量获取:
$1(第一个参数)、$2(第二个参数)、$#(参数总数)。 - 典型场景 :动态传递临时参数,如
./backup.sh /data 7(备份目录 + 保留天数)。 - 面试考法 :问 "脚本如何接收命令行参数?" → 答:用
$1、$2等位置变量。
2. 环境变量传递
- 用法 :
- 临时生效:
MYSTR=hello ./a.sh(仅当前脚本执行时可见) - 全局生效:
export MYSTR=hello(当前 Shell 及所有子进程可见)
- 临时生效:
- 特点 :环境变量可以被子进程继承,适合传递全局配置(如
PATH、HOME)。 - 面试考法 :问 "如何让环境变量在子脚本中生效?" → 答:用
export导出,或执行脚本时临时赋值。
三、面试核心对比
| 对比项 | 直接执行 ./a.sh |
. ./a.sh / source ./a.sh |
|---|---|---|
| 进程 | 新建子进程执行 | 当前进程执行 |
| 变量影响 | 不影响当前 Shell | 直接修改当前 Shell 环境 |
| 适用场景 | 独立脚本运行 | 加载配置、共享函数 / 变量 |
四、高频问答速记
-
问 :为什么直接执行脚本需要
chmod +x权限,而source不需要?→ 答 :直接执行需要脚本本身有可执行权限,source只是读取脚本内容在当前进程执行,不需要可执行权限。 -
问 :脚本执行时,
./a.sh $MYSTR和MYSTR=hello ./a.sh有什么区别?→ 答 :前者是把MYSTR的值作为命令行参数传递,后者是临时设置环境变量让脚本读取。 -
问 :如何让脚本里的函数在当前 Shell 中调用?→ 答 :用
source或.执行脚本,函数会加载到当前 Shell 环境。