Shell 脚本
1.什么是Shell
Shell有两层身份:
Shell 是一个“应用程序”(解析器)
它是用户和操作系统之间的“翻译官”,接收你输入的命令,然后交给内核执行。
常见的 Shell 应用程序有:
| Shell | 常见平台 | 特点 |
| ————————————————— | —————————————————— | —————————————————————— |
| bash (Bourne Again SHell) | Linux, macOS(默认) | 最常见,功能强,支持数组、字符串操作 |
| sh(Bourne Shell) | Unix, Linux | 最早的 Shell,兼容性高,功能略弱 |
| zsh(Z Shell) | macOS(默认从 Catalina 开始), Linux | 更丰富的交互体验,如自动补全、语法高亮 |
| csh / tcsh(C Shell) | Unix, BSD | 类似 C 语言语法,偏爱者较少 |
| fish(Friendly Interactive SHell) | macOS, Linux | 用户友好,现代语法,不兼容 bash 语法 |
| PowerShell | Windows(默认) | 面向对象,支持 .NET,跨平台(有 Linux 版本) |
| cmd.exe | Windows | 老式命令行,功能有限,不推荐编写复杂脚本 |Linux系统默认的解析器是bash,文本使用bash进行。
Shell 是一种“脚本语言”
可以把 Shell 命令写进一个文本文件里(脚本)。Shell 程序会 逐行解析并执行这些命令。这就是我们常说的 Shell 脚本,比如 .sh 文件。
脚本的本质是一个文件,通常是纯文本,里面写的是:按照某种“脚本语言”格式组织起来的指令序列。
shell脚本是一种脚本语言,我们只需使用任意文本编辑器,按照语法编写相应程序,增加可执行权限,即可在安装shell命令解释器的环境下执行
脚本的特点:- 不需要编译,解释器(如 bash)一行一行地“解释”运行。
- 启动快,适合做自动化任务。
- 可以修改后立即运行,不需要重新编译。
2.Shell工作原理
以bash为例
- 打开一个终端(Terminal)时,Shell 程序就启动了,它会显示一个提示符( $ )等待你输入命令。
比如输入:1
ls -l
解析命令(词法分析 & 语法分析)
Shell 会把你输入的命令进行解析,也叫做“解释”:- 拆分成命令和参数,比如:
命令:ls
参数:-l
- 检查语法有没有错。
- 判断是不是变量、重定向、管道等操作。
- 拆分成命令和参数,比如:
查找命令的位置
Shell 会去系统的环境变量中查找这个命令对应的可执行文件。
例如ls
实际是/bin/ls
这个程序,它会被找到并准备执行。创建子进程并执行命令
Shell 并不会自己“执行”命令,它会:
使用fork()
创建一个子进程;
在子进程中调用exec()
去执行你指定的程序(比如/bin/ls
);
自己继续等待子进程执行完。等待命令执行完成并返回结果
命令执行完后,会返回一个 退出状态码(Exit Status),告诉 Shell 是否成功。
如果成功,状态码是 0。
如果失败,是非零的数字。
Shell 再把程序输出的内容显示在你的终端上。等待下一条命令
一轮执行完后,Shell 又回到第一步,等你下一次输入。
3.Shell脚本编程
当你执行一个 .sh 脚本时,Shell 会:
- 打开脚本文件;
- 从上到下一行一行地执行;
- 每行执行过程其实跟上面的流程一样。
3.1。Shell脚本执行方式
- 直接执行 前提是要先赋予权限
1
./myscript.sh
1
chmod +x myscript.sh
- 用解释器运行脚本(不需要可执行权限)
1
bash myscript.sh
- 通过绝对路径或相对路径调用 或者
1
/home/user/scripts/myscript.sh
1
../scripts/myscript.sh
- 把脚本放入 PATH 中,像命令一样调用
3.2.两个特殊的脚本
- /etc/profile 是一个系统级的 Bash 启动脚本,用于为所有用户在登录时设置统一的环境变量和初始化操作。用户第一次登录 Bash Shell 时,该文件就会被自动执行。常用于设置公共环境变量、默认路径、语言设置,以及调用系统开机自动执行的脚本(如 /etc/profile.d/*.sh 中的内容)等。
- ~/.bashrc 是当前用户的 Bash Shell 启动配置文件, 是当前用户每次打开 Bash 终端时自动执行的脚本,用于配置环境变量、别名、自定义命令等
- 环境变量(比如 PATH)
- 别名(alias)
- Shell 提示符(PS1)
- 自定义函数
- 运行你想每次打开终端时自动执行的命令
- 当你真正“登录”进系统(比如 ssh 登录或 tty 登录),系统会运行一次登录型的 Shell,这时 /etc/profile 会自动跑一次。
- 如果你只是打开一个普通终端窗口,那跑的是非登录型 shell,它加载的是 ~/.bashrc 这类文件,而不是 /etc/profile。
- 如果你想让所有用户一登录就执行某个命令,就写在 /etc/profile;如果你想自己每次打开终端都有某些设置,就写在 ~/.bashrc。
3.3 Shell脚本编程语法
3.3.1.基础语法
一个最基本的 Shell 脚本长这样:1
2
3
# 输出Hello world
echo "Hello, world!"
#!/bin/bash
:指定脚本解释器,必须写,放在第一行。#
:注释,不会被执行。echo
:输出3.3.2 输入输出
1
2read -p "请输入姓名:" username
echo "你好,$username"3.3.3.变量
Shell 变量是用来存储数据的命名位置。每个变量都有一个名称(标识符)和一个对应的值。在 Shell 中,变量可以存储字符串、数字或任何有效的数据类型。- 定义和使用
1
2
3
4
5name="Alice" #定义变量,使用等号(=)进行赋值,等号两边不能有空格
echo $name #引用变量,使用变量的值需要在变量名前面加上美元符号($)
echo "Hi, $name!" #在双引号中引用变量也能被解析
echo ${name} #使用花括号增强可读性(推荐)
echo "Hello, ${name}!"- 变量名以字母、数字、下划线组成;不能以数字开头;
- 等号两边不能有空格
- 推荐用小写变量名,避免和系统变量冲突
系统变量
$HOME
当前用户主目录$USER
当前用户名$PWD
当前工作目录(print working dir)$SHELL
当前使用的 Shell 程序路径$PATH
可执行命令搜索路径
预设变量
$0
当前脚本或命令的名称。$1~$n
第1~n个参数$@
所有参数(每个独立)$*
所有参数(视为一个)$#
参数个数
例如:
作用域
Shell 变量的作用域通常是局部的。
|类型|定义方式|作用范围|
|—-|—-|—-|
|局部变量| name=”Alice” |当前 Shell 或函数|
|环境变量| export name=”Alice” |当前 Shell 和其子进程|
|函数局部变量| local name=”Alice” |仅在当前函数中|
- 变量的默认值写法(防止为空)
1
echo "Hello, ${name:-Guest}" # 如果name为空,就显示 Guest
- 删除变量
1
unset name
3.3.4. 数组
- 数组定义和访问
1
2
3
4MY_ARRAY=(value1 value2 value3)
echo ${MY_ARRAY[0]} # 输出第一个元素
echo ${MY_ARRAY[@]} # 输出所有元素
echo ${MY_ARRAY[*]} # 输出所有元素 - 遍历数组
1
2
3
4
5# 使用 for 循环遍历数组
for value in "${MY_ARRAY[@]}"
do
echo "value: $value"
done
3.3.5.运算符
- 算术运算符
以使用算术运算符进行数学运算:- +:加法
- -:减法
- *:乘法
- /:除法
- %:取模(求余)
- +:加法
- 逻辑运算符
逻辑判断:- -eq:等于
- -ne:不等于
- -gt:大于
- -lt:小于
- -ge:大于等于
- -le:小于等于
- 字符串运算符
用于字符串处理的运算符: - if语句
1
2
3
4
5
6
7age=18
if [ $age -ge 18 ]; then
echo "成年人"
else
echo "未成年人"
fi - case语句
1
2
3
4
5
6
7
8read -p "输入成绩等级:" grade
case $grade in
A) echo "优秀" ;;
B) echo "良好" ;;
C) echo "及格" ;;
*) echo "不及格" ;;
esac - for循环
1
2
3
4for i in 1 2 3 4 5
do
echo "数字:$i"
done - while循环
1
2
3
4
5
6count=1
while [ $count -le 5 ]
do
echo "计数:$count"
count=$((count+1))
done3.3.7. 函数
定义函数
1
2
3
4
5
6
7function_name() {
# 函数体(命令序列)
}
#或者 旧写法
function function_name {
# 函数体(命令序列)
}调用
1
2
3
4
5function my_func {
echo "Hello from function"
}
my_func- 函数传参
Shell 函数的参数和脚本参数一样,用 $1, $2, $3 表示第 1 个、第 2 个、第 3 个参数…1
2
3
4
5
6
7greet() {
echo "Hello, $1! You are $2 years old."
}
greet "Alice" 25
#输出 Hello, Alice! You are 25 years old. 返回值
Shell 函数的 return 只能返回 0~255 之间的整数(代表状态码)。1
2
3
4
5
6add() {
return $(($1 + $2))
}
add 3 4
echo $? # 用 $? 获取函数返回值(是 7)如果想返回字符串或其他数据,用 echo + 命令替换 的方式:
1
2
3
4
5
6add() {
echo $(($1 + $2))
}
result=$(add 3 4)
echo "结果是 $result"- 局部变量(防止变量污染)
1
2
3
4my_func() {
local name="Tom" # 仅在函数内部有效
echo $name
}