Shell 脚本是把一堆 Linux 命令按顺序写进一个文件里让计算机替你重复执行。从定时备份日志、批量改文件名到部署项目时一键起服务绝大多数运维自动化的起点都是一个.sh文件。这一篇从 Shebang 讲起把变量、注释和三种执行方式一次说清楚写完就能跑。一、第一个 Shell 脚本打开任意编辑器新建一个文件hello.sh#!/bin/bash# 这是我的第一个 Shell 脚本echoHello, Shell!nameBenothingecho你好$name保存后先看一下它的权限ls-lhello.sh预期输出新建的文件默认没有可执行权限-rw-r--r-- 1 user user 68 Sep 16 10:00 hello.sh带x的那一位现在是-意思是不能直接执行。加上可执行权限再跑chmodx hello.sh ./hello.sh预期输出Hello, Shell! 你好Benothing到这里你已经写完并运行了第一个脚本。接下来逐个拆解每一行在干什么。二、Shebang第一行#!的作用脚本第一行固定长这样#!/bin/bash#!两个字符合起来叫 Shebang也叫 hashbang。它的作用是告诉系统这个脚本该用哪个解释器来跑。你写的.sh文件本质是纯文本内核自己看不懂。当你执行./hello.sh时内核读到 Shebang就会把文件剩下的内容交给/bin/bash这个程序去逐行解释。常见写法还有写法含义#!/bin/bash用 bash 解释器最常用#!/bin/sh用系统默认的 sh通常指向 dash 或 bash#!/usr/bin/env bash从 PATH 里找 bash移植性最好#!/usr/bin/python3这个文件其实是 Python 脚本第三种#!/usr/bin/env bash在团队协作和多台服务器之间最稳因为不同发行版 bash 的安装位置可能不一样写死/bin/bash在某些精简系统上会报bad interpreter。⚠️ 常见错误Shebang 前面有空行或 BOM第一行必须真的是第一行。Windows 编辑器保存成 UTF-8 with BOM 时文件开头会多三个不可见字节内核会把 Shebang 识别成乱码报/usr/bin/env: bash\r: No such file or directory。用cat -A hello.sh能看到^M或M-oM-;M-?这类隐藏符号。写成#! /bin/bash#!和路径之间不要有空格虽然有些系统容忍但不保证。忘了 Shebang 也能跑用bash hello.sh这种方式启动时Shebang 被当普通注释跳过了所以你删掉第一行也能执行。但一旦改成./hello.sh直接执行就会失败Shebang 不能省。三、变量定义、引用与赋值规则Shell 里定义变量非常直接变量名值等号两边不能有空格。greeting早上好count3引用变量时在名字前面加$echo$greetingecho今天是第$count天预期输出早上好 今天是第 3 天赋值的几条铁律写法是否合法说明nameTom合法等号两边无空格name Tom报错中间有空格shell 会把name当命令nameTom合法无空格的值可以不加引号nameTom Lee合法带空格的值必须加引号name Tom报错等号左边不能有空格namevalue合法行尾空格无所谓为什么等号两边不能有空格因为 shell 把空格当命令的分隔符。name Tom会被拆成三个 token命令name、参数、参数Tomshell 去找一个叫name的命令自然找不到。${name}和$name的区别变量名后面紧跟普通字符时花括号能帮 shell 界定边界fruitappleechoI like${fruit}s预期输出I like apples如果写成echo I like $fruitsshell 会去找一个叫fruits的变量结果是空的。花括号在拼接字符串、数组下标、路径拼接时几乎是必选项。命令替换把命令结果存进变量反引号和$(...)都能把一条命令的输出抓回来now$(date%Y-%m-%d)echo今天日期是$now预期输出今天日期是 2026-09-16推荐用$(...)它可以嵌套反引号嵌套时要写一堆反斜杠可读性很差。四、注释怎么写单行注释用#从#到行尾全是注释。Shebang 那一行虽然也是#开头但它是特例内核会特殊识别。#!/bin/bash# 整行都是注释echo这行会执行# 这行后面也是注释Shell 没有原生的多行注释。常见做法是用一个什么都不做的函数或 here document 来折叠一段但最实用的方式就是每行前面都加#IDE 里按Ctrl/一键注释一片。五、执行脚本的三种方式同一个脚本三种跑法行为差别不小。方式命令是否需要可执行权限是否在当前 shell 生效直接执行./hello.sh需要chmod x子进程解释器调用bash hello.sh不需要子进程source 调用source hello.sh或. hello.sh不需要当前进程前两种方式都会fork 一个子 shell来跑脚本脚本里定义的变量、cd切换目录跑完就消失不会影响你当前终端。第三种source是在当前 shell 里逐行执行脚本里export的变量、cd进的目录会一直留着。这个区别在写环境变量配置脚本时是关键安装软件后改PATH你必须source那个配置文件改动才对当前终端生效直接执行一个.sh去改PATH跑完什么都没发生。完整示例对比三种执行方式写一个test_env.sh#!/bin/bashmy_varfrom scriptcd/tmpecho脚本内 my_var$my_varecho脚本内 pwd$(pwd)分别用三种方式跑bashtest_env.shecho跑完后 my_var$my_var, pwd$(pwd)预期输出脚本内 my_varfrom script 脚本内 pwd/tmp 跑完后 my_var, pwd/home/user换成 sourcesourcetest_env.shecho跑完后 my_var$my_var, pwd$(pwd)预期输出脚本内 my_varfrom script 脚本内 pwd/tmp 跑完后 my_varfrom script, pwd/tmp注意source把当前目录都改到/tmp了用完记得cd回去。六、echo 输出与引号的初步认知echo后面跟单引号和双引号行为完全不同whoBenothingecho双引号:$whoecho单引号: $who预期输出双引号: Benothing 单引号: $who双引号会先把里面的变量展开单引号原样输出连$都不解释。写脚本时这个区别每天都在踩后面讲变量那一篇会展开。七、知识扩展Shebang 背后内核到底干了什么你在 shell 里敲./hello.sh并回车整条链路是这样的当前 shell比如你登录后的 bash通过fork()复制出一个子进程。子进程调用execve(./hello.sh, ...)把自己替换成新程序。内核打开hello.sh看文件头两个字节是不是#!。是内核就把这一行剩下的字符串当作解释器路径再execve(/bin/bash, /bin/bash, ./hello.sh)。bash 启动后逐行读取脚本把每一行拆成命令、参数、变量再去执行。所以 Shebang 不是 shell 的功能是操作系统内核的约定。这也解释了为什么 Windows 上没有 Shebang——Windows 的CreateProcess根本不认这两个字符。理解这条链路还能解释一个常见困惑为什么./hello.sh和bash hello.sh结果有时候不一样因为前者是内核按 Shebang 找解释器后者是你手动指定 bashShebang 那行被当注释。如果脚本第一行写的是#!/bin/zsh而系统没装 zsh./hello.sh直接报No such file or directory但bash hello.sh能跑——它绕开了 Shebang。动手把这一篇里的hello.sh、test_env.sh都敲一遍三种执行方式的差异是后面所有脚本知识的地基值得花十分钟亲手验证。