Shell 脚本语法规则速查手册
适用于 Bash / Shell 脚本编写(Linux 环境)
一、文件基础规则
1.1 文件扩展名与权限
# 推荐扩展名: .sh (不是必须,但便于识别)myscript.sh# 给脚本加执行权限(只需一次,之后可以 ./运行)chmod+x myscript.sh# 运行方式./myscript.sh# 方式一:直接运行(需要执行权限 + Shebang)bashmyscript.sh# 方式二:指定解释器运行(不需要执行权限)1.2 第一行必须写 Shebang(必写)
#!/bin/bash # ✅ 推荐:用 Bash 解释器(功能最全)#!/bin/sh # ✅ 可用:用 POSIX Shell(兼容性好,功能少)#!/usr/bin/env bash # ✅ 可用:自动找系统里的 bash(跨平台推荐)# ❌ 错误:Shebang 不能写在第2行及以后# (第1行空了)#!/bin/bash # ← 这样写无效!二、注释规则
# 这是一行注释(# 后面的内容全被忽略)echo"hello"# 这是行尾注释,# 前面是代码,后面是说明# Shell 没有多行注释!想注释多行就每行都写 ## 第一行注释# 第二行注释# 第三行注释# ❌ 不要用这些:Shell 不认/* 这样的多行注释 Shell 会报错 */ // 双斜线也不是注释三、最容易踩的坑(必背)
| 坑点 | 正确写法 | 错误写法 | 报错信息 |
|---|---|---|---|
=两边不能有空格 | name="tom" | name = "tom" | name: command not found |
[ ]两边必须有空格 | if [ -f a.txt ] | if [-f a.txt] | [-f: command not found |
取变量值要加$ | echo $name | echo name | 直接输出字母 name |
每个if必须有fi配对 | if ... then ... fi | 漏写fi | syntax error: unexpected end of file |
💡理解
$的本质:$可以近似理解为 C 语言 / C++ 里的解引用(*),本质上就是变量展开——解析脚本时,Shell 会把$变量名替换成变量里存的值。但要注意:Shell 脚本里没有内存和指针的概念,$只是单纯的文本替换,不是去访问某个内存地址。
四、变量
4.1 定义与取值
# 定义变量:等号两边绝对不能有空格!name="小明"age=18ip="127.0.0.1"# 取值:加 $ 符号echo$name# 输出:小明echo${name}# 等价写法,推荐用这个,更清晰# 错误示例(注意空格!)name="小明"# ❌ 等号两边有空格 = 灾难4.2 双引号 vs 单引号
city="北京"# 双引号:里面的变量会被替换(解析 $)echo"我住在$city"# 输出:我住在 北京# 单引号:原样输出,不会解析 $echo'我住在 $city'# 输出:我住在 $city# 推荐:用变量的地方都加双引号,防空格file="my photo.jpg"rm"$file"# ✅ 正确:能识别空格rm$file# ❌ 危险:会当成 rm my 和 photo.jpg 两个文件4.3 常用内置变量
| 变量 | 含义 | 示例值 |
|---|---|---|
$0 | 脚本本身的文件名 | ./ser_start.sh |
$1$2$3 | 第1/2/3个命令行参数 | ./s.sh a b→$1=a, $2=b |
$# | 参数的个数 | ./s.sh a b→2 |
$@ | 所有参数(数组形式) | 所有参数列表 |
$$ | 当前脚本的进程ID | 12345 |
$? | 上一条命令的返回值(0=成功,非0=失败) | 0或1 |
$(pwd)或\pwd`` | 取命令的执行结果 | /home/user/project |
五、条件判断(if / else)
5.1 基本语法
if[条件]then# 条件成立时执行elif[条件2]then# 条件2成立时执行else# 都不成立时执行fi# ← 不能漏!fi 是 if 反过来写5.2 常用判断条件
文件判断:
if[-f"service.conf"]# 文件存在且是普通文件?if[-d"output"]# 目录存在?if[-x"service"]# 文件有执行权限?if[-s"a.txt"]# 文件存在且大小 > 0?if[!-f"a.txt"]# 文件不存在?(! 取反)数值比较:
| 写法 | 含义 | 示例 |
|---|---|---|
-eq | 等于 equal | if [ $a -eq 10 ] |
-ne | 不等于 not equal | if [ $a -ne 10 ] |
-gt | 大于 greater than | if [ $a -gt 5 ] |
-lt | 小于 less than | if [ $a -lt 5 ] |
-ge | 大于等于 | if [ $a -ge 10 ] |
-le | 小于等于 | if [ $a -le 10 ] |
# 示例:判断数值age=18if[$age-ge18]thenecho"成年人"fi字符串比较:
if["$a"="$b"]# 字符串相等(= 两边有空格!)if["$a"!="$b"]# 字符串不相等if[-z"$a"]# 字符串为空(长度为0)if[-n"$a"]# 字符串不为空# 示例name="小明"if["$name"="小明"]thenecho"你好,小明"fi逻辑运算:
if[$a-gt5]&&[$a-lt20]# && = 并且(两个都真才真)if[$a-lt5]||[$a-gt20]# || = 或者(一个真就真)六、循环
6.1 for 循环
# 遍历列表foriin12345doecho"数字:$i"done# 遍历文件forfilein*.cppdoecho"找到C++文件:$file"done# C 语言风格(需要 bash)for((i=0;i<10;i++))doecho$idone6.2 while 循环
n=1while[$n-le5]doecho"第$n次"n=$((n+1))# 变量自增done七、函数
# 定义函数greet(){echo"你好,$1!"# $1 = 函数的第一个参数}# 调用函数(不需要括号!)greet"小明"# 输出:你好,小明!greet"小红"# 输出:你好,小红!# 带返回值的函数(返回值只能是 0-255 的数字)add(){sum=$(($1+$2))return$sum# 只能返回数字}add35echo"结果:$?"# 用 $? 取返回值:8💡函数参数怎么传?Shell 定义函数时,括号里不用写形参(
greet()括号是空的);调用函数时在函数名后面直接写实参即可,函数内部通过$1、$2这类特殊变量拿到第 1、2 个参数(和脚本接收命令行参数是同一套机制)。
八、数组
# 定义数组arr=("apple""banana""cherry")# 取值echo${arr[0]}# 第1个:appleecho${arr[1]}# 第2个:bananaecho${arr[@]}# 全部元素:apple banana cherryecho${#arr[@]}# 长度:3# 遍历数组forfruitin${arr[@]}doecho"水果:$fruit"done九、输入输出
9.1 输出 echo
echo"普通文本"echo-e"这里会\t转义\n特殊字符"# -e 启用转义:\t 制表 \n 换行echo-n"不换行输出"# -n 输出后不自动换行9.2 输入 read
echo"请输入你的名字:"readusernameecho"你好,$username"# -p 可以直接加提示read-p"请输入端口:"portecho"端口号是$port"9.3 重定向
# 把输出写入文件(覆盖写)echo"hello">a.txt# 追加写入(不覆盖)echo"world">>a.txt# 错误输出一起重定向make1>log.txt2>err.txt# 1=标准输出 2=错误输出make>log.txt2>&1# 错误也写到同一个文件十、常用命令写法速查
| 需求 | 写法 |
|---|---|
| 取命令结果 | today=$(date)或today=\date`` |
| 数字运算 | sum=$(( a + b )) |
| 切换到脚本所在目录 | cd "$(dirname "$0")" |
| 判断命令是否执行成功 | if make; then echo "ok"; fi |
| 上一条命令失败就退出 | set -e(脚本第二行加一次即可) |
| 未定义的变量报错 | set -u(防拼写错误) |
| 一行写多条命令 | make clean && make(前一个成功才执行后一个) |
| 一行写多条命令 | make clean; make(不管成功与否都执行) |
| 后台运行 | ./service & |
| 暂停/休眠 | sleep 3(休眠3秒) |
十一、脚本推荐模板(直接抄用)
#!/bin/bashset-e# 遇到错误立即退出,避免错误扩散# set -u # 用到未定义的变量时报错(调试阶段建议打开)# ==============================# 函数区# ==============================log_info(){echo"[INFO]$1"}log_error(){echo"[ERROR]$1"}# ==============================# 主逻辑# ==============================# 切换到脚本所在目录SCRIPT_DIR="$(cd "$(dirname"$0")"&&pwd)" cd "$SCRIPT_DIR" log_info "脚本开始运行..." # 你的代码写在这里 log_info "脚本运行完成 ✓"十二、常见报错与解决
| 报错信息 | 原因 | 解决 |
|---|---|---|
Permission denied | 没有执行权限 | chmod +x xxx.sh |
bad interpreter | Shebang 写错或第一行不是它 | 第一行写#!/bin/bash |
command not found | =两边加空格了 / 命令拼错了 | name="a"不要空格 |
syntax error | if漏了fi/for漏了done | 检查配对的结束关键字 |
unary operator expected | [里变量为空没加引号 | [ "$a" = "x" ]加双引号 |
too many arguments | 变量值里有空格没加引号 | 所有变量取值都加"$var" |
[: missing ] | ]前面没空格 | [ -f a.txt ]注意空格 |