算起来我跟 Linux 打交道已经十几年了。这些年见过不少新人捧着几百页的 Shell 教程翻到一半就放弃也有朋友在面试前突击“Linux 常用命令”结果一考到脚本逻辑就卡壳。2026 年再看 Shell 编程它依然是 Linux 运维、嵌入式开发、DevOps 流水线里最刚的基础技能一点没过时。这篇文章不打算写严谨的教科书而是把我从“只会敲命令”到“敢把脚本放到生产环境跑”这个过程里最值得记的东西整理出来。内容围绕 Shell 命令行、Shell 脚本语法、常见坑和面试高频题展开刚入门的可以跟着敲一遍有基础的可以当避坑手册翻。1. 为什么2026年还要学Shell——先搞清楚它到底解决什么问题1.1 Shell到底是什么它和Linux系统是什么关系很多人会把“Linux”和“Shell”混在一起说其实它们是两层东西。Linux 内核负责管理硬件、进程、内存它是系统真正干活的人。但内核不具备跟人类直接交流的能力Shell 就是那个翻译官你在 Shell 命令行里输入一条指令Shell 负责解析这条指令、调用内核去执行再把结果返回给你。用生活里的事打比方Linux 内核像一家餐厅的后厨Shell 就是前台点单的菜单和收银系统。你不需要知道后厨怎么洗菜、怎么开火只要在菜单上选好菜后厨就会照做。菜单能表达的选项越多、越准确你能吃到的菜就越符合预期。Shell 也是一样命令写得好不好决定了你能否准确驱动这台机器。Shell 本身也是一个程序常见的有 bash、zsh、sh、dash 等。大多数 Linux 发行版默认的 Shell 是 bash所以后面所有内容我都以 bash 为主。理解了这个关系你就知道为什么“会敲几条命令”和“会写 Shell 脚本”是两种能力前者只是会用菜单点菜后者才是懂得怎么配出一桌完整的席面。到了 2026 年AI 写脚本的能力确实很强但你得能看懂它给出的脚本、能判断逻辑对不对、能根据实际环境调整参数这些基本功仍然绕不开。1.2 哪些岗位和场景最需要Shell技能Shell 不是某一类人的专属工具但有几类场景特别靠它吃饭。运维工程师不用多说服务器批量操作、日志清理、定时备份、监控告警基本都是 Shell 脚本在支撑。后端开发写流水线时部署脚本里大量出现bash关键字排查线上问题时几个命令组合就能定位瓶颈。嵌入式开发也躲不开交叉编译环境的配置、烧录脚本的编写很多都依赖 Shell。就算你只是自己拿 Linux 当日常系统用批量改文件名、压缩解压、批量转换格式Shell 都能帮你省下大量重复劳动。另外还有个新趋势现在很多 AI 编程工具能直接帮你生成 Shell 脚本你只需要描述需求。但我在实际帮别人看脚本的时候发现AI 生成的脚本经常有“看起来很合理但一跑就废”的问题比如忘了处理路径带空格的情况、用了不兼容的语法、没有考虑目标机器上根本没有某个命令。这说明什么AI 降低了写作门槛但没有降低理解门槛。你越懂 Shell 的基础逻辑就越能把 AI 变成你的帮手而不是被它生成的脚本坑一把。1.3 选哪种Shellbash、sh、zsh怎么定先给结论日常使用和写脚本我都推荐用 bash。虽然 zsh 在交互式体验上更舒服有自动补全、主题美化但 bash 的兼容性和可移植性最好。很多 Linux 服务器的默认 Shell 就是 bash脚本写出来丢到任何一台机器上都能跑这才是脚本该有的样子。Shell特点适合场景bash功能全、兼容性好、最常用默认选择写脚本首选zsh交互体验佳、补全强大个人环境使用写脚本慎用sh最轻量、兼容 POSIX 标准嵌入式环境、极小系统dash启动快、占用低系统初始化脚本有个细节建议脚本第一行写#!/usr/bin/env bash而不是#!/bin/bash。原因是你不知道脚本将来会跑在哪台机器上bash 可能装在/usr/bin/bash也可能在/bin/bash。env命令会自动去 PATH 里找 bash 的位置可移植性更好。在命令行里先执行echo $SHELL看看当前环境用的是哪个 Shell执行bash --version确认版本号这是入门的第一步。提示你交互式命令行里的 Shell 和脚本执行时用的解释器不一定是同一个。脚本开头的#!注释行才是真正决定脚本用哪个解释器执行的地方。2. 环境准备零基础搭好Shell练习场2.1 三种快速搞到Linux环境的方式学 Shell 最怕“只看不练”。好在你有很多种方式能拿到一个 Linux 环境不用买服务器也能开始。第一种是想最快上手的人直接用在线 Linux 终端。很多学习网站提供免费的网页版 Linux 沙盒打开浏览器就能敲命令适合验证某个语法、临时跑个小脚本。这种方式的缺点是不能持久保存数据环境被重置是常事所以只适合入门体验。第二种是本地虚拟机。去发行版官网下载 ISO 镜像用虚拟机软件创建一台虚拟机设置好内存和磁盘挂载镜像安装。这种方式最接近真实服务器环境你想怎么折腾都行装坏了重新再来就是。安装时注意给磁盘留够空间最小 20G 起步内存建议 2G 以上不然跑图形界面会比较卡。如果想追求性能也可以直接装双系统但对新手来说虚拟机更安全毕竟不影响日常使用的主系统。第三种是云主机。很多云服务商对新用户有免费试用额度申请一台最低配置的 Linux 云主机用处很大你可以在上面部署服务、跑定时任务甚至搭一个能随时 SSH 进去的环境。一台云主机能玩的东西比本地虚拟机多得多等你想进一步学 Linux 运维时这是必经之路。我自己走的路是“在线终端入门然后虚拟机最后云主机”。循序渐进的好处是不会一上来就被安装过程劝退。记住一点环境只要能敲命令就行不要在这个阶段纠结发行版选 Ubuntu 还是 CentOSShell 的基本语法在所有发行版上几乎都一样。2.2 第一个命令和第一行脚本环境就绪后先不要急着学一堆命令我建议你按下面的顺序走一遍建立“输入命令 — 看结果 — 理解输出”的感觉。pwd # 查看当前目录 ls -l # 查看目录下文件详情 echo hello shell # 打印一行文本 date # 查看当前系统时间这些命令本身很简单但有两个概念必须立刻建立一个是pwd告诉你现在人在哪里Shell 的一切操作都跟“当前路径”有关另一个是date的输出可以当作变量存起来这会让很多运维脚本变得非常灵活。接着写第一个脚本。在你的用户目录下建一个learn目录进入后创建hello.sh文件mkdir -p ~/learn cd ~/learn vim hello.sh文件内容写#!/usr/bin/env bash echo hello shell保存退出后有两种方式执行它。一种直接交给编程解释器bash hello.sh另一种是给它执行权限后直接运行chmod x hello.sh ./hello.sh。第二种方式更常用因为脚本写多了之后你希望它像一个普通命令一样被调用而不是每次前面都要写bash。注意新手最容易在这里困惑的是权限问题。直接运行./hello.sh如果提示Permission denied说明你没有执行权限执行chmod x就能解决。用bash hello.sh则不需要执行权限因为是 bash 程序来读这个文件而不是脚本自己运行。2.3 编辑器选择与脚本文件的规范细节写脚本离不开编辑器。我的建议是初学阶段至少要会vim的基本操作打开、编辑、保存、退出因为服务器上通常没有图形界面vim 是最保底的编辑器。如果觉得 vim 难记也可以先用 nano操作提示直接显示在底部对新手更友好。在本地开发机上Visual Studio Code 装一个 Remote SSH 插件直接远程编辑服务器上的脚本体验很好。有几个脚本文件规范我建议从一开始就养成习惯。第一文件编码统一用 UTF-8不要用带 BOM 的编码否则脚本第一行会变成一个不可见字符导致#!/usr/bin/env bash失效。第二行尾符必须是 LF不能是 CRLF。如果你把 Windows 下编辑的脚本传到 Linux 上经常会出现“命令未找到”的诡异报错原因就是每行末尾多了一个回车符。第三脚本文件名不用刻意加.sh后缀Linux 不靠扩展名识别文件类型加了只是让人看着舒服。脚本内部格式同样有讲究。统一用set -euo pipefail这行开头是我强烈推荐的习惯。它的含义我们后面再展开但先记下一句话它能让脚本在出错时及时停下来而不是带着错误一路跑下去最终把环境搞乱。3. 核心语法一次打通从命令到脚本的关键跨越3.1 变量、引号和通配符这些基础语法最骗人先说变量。Shell 变量的定义规则很简单变量名和值之间不能有空格。写成name shell是错的Shell 会以为你要执行一个叫name的命令写成nameshell才是赋值。用变量的时候要在前面加$比如echo $name。加不加引号结果差别很大namea b c echo $name # 输出: a b c空格被默认当作分隔符 echo $name # 输出: a b c作为一个整体第二行不加引号时$name会被拆成三个单词传给 echo命令虽然能跑但很多场景下这种拆词会带来灾难。最典型的就是删除文件如果文件名里有空格你写rm $filerm 会认为你有三个文件要删。所以凡是变量可能包含空格、或者路径可能包含空格请一律用双引号包起来rm $file。引号有三类单引号、双引号、反引号它们各有分工。单引号内所有字符都按字面处理变量不会被展开双引号内$变量会被展开但*这类特殊字符仍然保留字面意义反引号现在已经不推荐用了统一用$(命令)代替命令替换。举例nameworld echo hello $name # 输出: hello $name echo hello $name # 输出: hello world echo today is $(date) # 输出: today is ...第三部分是通配符。*匹配任意长度的任意字符?匹配单个字符[abc]匹配方括号里的任一字符。它们是路径展开的工具用于批量操作非常方便。比如ls *.log会列出当前目录下所有以.log结尾的文件。注意通配符是在命令执行前由 Shell 展开的不是程序自己去匹配。理解了这一点你就知道为什么for f in *.log在目录为空时会得到一个字面*.log而不是报错我们后面有一节专门讲这个坑。3.2 if判断和for循环让脚本有逻辑的关键脚本和交互式敲命令最大的区别是脚本可以走逻辑分支。if是分支的核心但 Shell 的判断语句有个特别容易让新手困惑的语法点方括号两边必须有空格。if [ -f $file ]; then echo 文件存在 else echo 文件不存在 fi这里的[其实是一个命令叫作 test所以它和后面参数之间必须有空格。-f表示判断是否是一个普通文件-d判断目录-z判断字符串是否为空。字符串比较用或!数值比较用-eq、-ne、-gt、-lt这个区分也是高频考点。如果你用的是 bash我更推荐写双中括号[[ ... ]]。它可以在判断式内部直接用和||还能用~做正则匹配可读性高很多。唯一要注意的是[[ ]]是 bash 的关键字不是所有 Shell 都支持所以脚本第一行确认是 bash 才这么写。循环方面最常见的两个是for和while。for循环尤其常用一句话就能概括把集合里的元素挨个取出来做同样的事。for i in {1..5}; do echo 第 $i 次 done想遍历当前目录下所有.sh文件也是这样for f in *.sh; do echo 找到脚本: $f donewhile read是处理文件每一行的经典写法while read line; do echo 行内容: $line done /etc/hosts一个很多人不知道的细节管道符|会开启子 Shell所以在管道右侧对变量做的修改在管道结束后不一定会保留。处理文件行时尽量用上面的 文件方式而不是cat file | while read后者容易踩变量丢失的坑。3.3 shift、export与变量作用域理解父子Shell的差别很多人在 Shell 里搞不清“为什么这个变量在脚本里设了外面却看不到”。这里面的核心是变量的作用域概念。默认情况下Shell 变量只在当前 Shell 进程里有效。你在终端里执行export之前定义的变量之后启动的子进程是看不到的执行bash -c echo $test_var输出是空的。一旦执行了export test_varhello这个变量就会进入环境变量区子进程就能读到了。a仅当前生效 export b子进程也能看到 bash -c echo a$a; echo b$b # 输出: a # 输出: b子进程也能看到还有一个常见操作是source比如source /etc/profile。它和直接执行脚本的区别是 source 会在当前 Shell 进程里执行而./script.sh或者bash script.sh会启动一个新的子进程去执行。所以想要脚本里的变量修改在当前环境保留下来就用 source想让脚本在隔离环境里跑完就退出就用直接执行。shift命令是处理脚本参数的利器。脚本每次执行时参数$1、$2、$3依次对应你传给它的第 1、2、3 个参数。执行一次shift所有参数会整体左移原来的$2变成新的$1。这在你做循环解析参数时非常有用while [ $# -gt 0 ]; do echo 当前处理参数: $1 shift done这段代码会把所有传入参数一个一个地打印出来每处理完一个就移走一个直到全部处理完。$#是参数个数$是所有参数列表。写脚本时经常用这种循环去解析类似--input file.txt这样的自定义参数非常灵活。3.4 批量重命名实战用Shell处理文件一次学会参数扩展文件批量重命名是 Shell 最实用的场景之一也是很多人第一次感受到“脚本比手动操作爽”的地方。假设目录里有几十个.txt文件你想把扩展名换成.md一行 for 循环就解决for f in *.txt; do mv $f ${f%.txt}.md done这里的${f%.txt}是 Shell 的参数扩展作用是从变量f的末尾开始删除最短匹配.txt的部分。%是从右往左删#是从左往右删两个符号分别对应最短匹配双写%%和##则是最长匹配。理解这几个符号批量重命名时基本不用求助外部工具。再进阶一点批量按序号重命名图片i1 for f in *.jpg; do mv $f $(printf img_%03d.jpg $i) i$((i 1)) doneprintf %03d会把数字补成三位比如 001、002保证排序顺序正确。这里我特别强调补零是因为如果文件名成了 1、2、10 这样的数字按字符串排序时 10 会排在 2 前面顺序会乱。带上补零看着麻烦实际使用中能省下很多后续麻烦。提示所有批量操作尤其是删除、移动、重命名第一遍跑的时候强烈建议先“干跑”。具体做法是把mv改成echo mv只打印将要执行的命令确认无误后再真正执行。我见过太多人直接跑了批量命令跑完才发现文件名被改坏了又没有备份只能干瞪眼。4. 三个可以直接抄的实战脚本4.1 定时备份脚本带日期文件名和自动清理运维里最刚的需求就是备份。先看一段我在服务器上用的备份脚本模板结构不复杂但非常完整#!/usr/bin/env bash set -euo pipefail BACKUP_ROOT/data/backup SOURCE_DIR/var/www DATE$(date %F) mkdir -p $BACKUP_ROOT/$DATE tar czf $BACKUP_ROOT/$DATE/www_$(date %H%M%S).tar.gz -C /var www find $BACKUP_ROOT -type d -mtime 7 -exec rm -rf {} \; echo [$(date %F-%T)] 备份完成目录: $BACKUP_ROOT/$DATE解释一下关键点。第一行set -euo pipefail是三件套-e表示遇到非零退出码就立刻终止脚本-u表示使用未定义变量时报错-o pipefail表示管道中任何一个命令失败都会让整条管道返回失败。这三条组合起来能避免脚本在出错后继续执行把问题隐藏到很晚才暴露。备份文件名里带上日期和时分秒是为了防止同一天多次备份时文件互相覆盖。date %F得到的是2026-05-12这样的日期格式date %H%M%S得到143022这样的时间串合在一起就是精确到秒的标识。最后一行find ... -mtime 7 -exec rm -rf的意思是找出修改时间超过 7 天的备份目录并删掉。这样备份根目录不会无限膨胀。很多新手只写备份不写清理结果磁盘被备份文件塞满运维事故就这么来的。配合 crontab 定时执行在终端输入crontab -e加入一行0 2 * * * /opt/scripts/backup_site.sh /var/log/backup.log 21意思是每天凌晨 2 点执行一次备份脚本标准输出和错误输出都写入日志文件。排错时这个日志非常关键。注意cron 的环境变量和交互式终端不一样PATH 很精简所以脚本内部建议写绝对路径不要依赖相对路径。4.2 分析Nginx日志统计访问最多的IP和状态码日志分析是 Shell 文本处理能力的经典体现。生产环境上一条 Nginx 访问日志长这样192.168.1.10 - - [12/May/2026:10:15:32 0800] GET /index.html HTTP/1.1 200 5321想看谁访问得最频繁一条组合命令就够了awk {print $1} access.log | sort | uniq -c | sort -nr | head -20这条命令的链路是awk 提取每行的第一列也就是 IP 地址sort 排序让相同 IP 排到一起uniq -c 统计每个唯一值出现次数sort -nr 按数字大小倒序排列head -20 取前 20 名。这也是一道面试必考题很多面试官让你“手写一个统计访问 IP 次数”参考答案就是这个。状态码分布也能一条命令看明白awk {print $9} access.log | sort | uniq -c | sort -nr想分析某个时间段内的日志用 grep 先过滤再统计grep 12/May/2026:10: access.log | awk {print $1} | sort | uniq -c | sort -nr | head -10这套组合看起来短但每个命令都承担一环理解了管道思想就不觉得难。日志分析这个场景也最能训练 Shell 思维把一个大任务拆成一个个只做一件事的小命令然后用管道把上一环的输出接到下一环的输入。这个思路写脚本同样适用。4.3 磁盘内存小监控超过阈值就告警写一个监控脚本检查根分区磁盘使用率和内存使用率超过阈值就写告警。生产环境里Shell 监控脚本通常配合 cron 每 5 分钟跑一次。#!/usr/bin/env bash set -euo pipefail DISK_THRESHOLD80 MEM_THRESHOLD90 DISK_USED$(df / | awk NR2 {print $5} | sed s/%//) MEM_USED$(free | awk /Mem:/ {printf %d, $3/$2 * 100}) if [ $DISK_USED -gt $DISK_THRESHOLD ]; then echo $(date) 警告: 根分区使用率 ${DISK_USED}% 超过阈值 ${DISK_THRESHOLD}% /var/log/monitor.log fi if [ $MEM_USED -gt $MEM_THRESHOLD ]; then echo $(date) 警告: 内存使用率 ${MEM_USED}% 超过阈值 ${MEM_THRESHOLD}% /var/log/monitor.log fi这里最需要注意的是df和free的输出格式在不同 Linux 发行版上可能有差异。写脚本前先手动跑一下df /和free看输出长什么样再决定 awk 怎么取数。我在不同机器上就遇到过 awk 取列位置不同的情况直接抄网上脚本很容易翻车。告警方式可以按自己的环境扩展。有邮件服务就用mail -s发邮件有企业聊天机器人的就调 webhook 接口。核心逻辑都是一样的检查数据、跟阈值比较、触发动作。这个脚本结构是监控类脚本的标准模板理解它以后看市面上其他监控脚本基本不会发懵。5. 常见坑与面试题实录5.1 六个我踩过的Shell坑每一个都很经典Shell 编程门槛低但细节陷阱特别多。下面这六个坑我全部在真实环境里踩过整理成速查表帮你一次性避开。现象原因解决方案command not found且命令名带^MWindows 换行符 CRLF 未转换运行dos2unix或sed -i s/\r$// script.shif [ $a 1 ]报错[: too many arguments变量 $a 为空拆词后导致参数错乱变量加双引号[ $a 1 ]rm *.tmp提示No such file目录里没有匹配项通配符原样传给 rm加shopt -s nullglob或先判断脚本用了set -e仍继续执行某些命令的错误码被忽略检查 if 条件里的命令合理使用 循环里统计的变量结果为空管道右侧是子 Shell变量修改不传回用进程替换 (cmd)或优化写法文件名有空格批量操作失败变量未加引号空格导致拆词所有变量引用都加双引号command not found ^M这个坑我印象最深。有一次写完脚本传上服务器怎么执行都报命令找不到检查脚本内容明明是对的。后来用cat -A script.sh一看每行结尾都有^M$才知道是 Windows 的换行符在捣乱。这之后我养成了习惯脚本写完先跑一遍cat -A或者直接用 shellcheck 检查。5.2 面试常问Shell题这么答显得有水平Linux 和 Shell 是后端、运维岗位面试的必考模块。结合我当面试官的经验分享几个高频题目的答题思路。第一类是命令组合题比如“找出某个目录下占用最大的文件”。参考思路先du -sh或du -ah查看目录空间占用再用sort -h按人类可读大小排序head -20取前 20。面试官更看重你能否把大任务拆成小命令而不是背一个神器命令。第二类是概念辨析题比如“硬链接和软链接有什么区别”。硬链接指向同一个 inode删除源文件不影响链接软链接类似快捷方式源文件不存在后链接失效。回答时先讲本质再举个使用场景例如软链接常用于把程序版本指向不同目录。第三类是变量相关题比如“$和$*有什么区别”。$把每个参数当作独立个体$*把所有参数拼成一个字符串。写脚本时遍历参数推荐用$不容易被空格坑到。还有一个高频考点是“Linux 查看系统状态的命令”。用来查 CPU 用top或vmstat内存用free -h磁盘用df -h端口监听用ss -tlnp。我会建议你把这些命令练熟因为面试官很可能顺着你的回答继续追问比如“怎么看哪个进程占了最多内存”这类问题。5.3 排查Shell脚本的实用方法bash -x和shellcheck脚本写出来一定会出错出错之后怎么快速定位是区分新手和老手的关键。我的排查流程一般是先确认报错信息再看退出码最后用跟踪模式跑一遍。在正常执行命令前加bash -x脚本的每一步都会被打印出来变量展开后的实际值也一目了然。看下面这段输出$ bash -x test.sh nameworld echo hello world hello world开头的行就是实际执行的命令展开后的样子直接可见。如果脚本在某个环节报错-x能帮你准确定位是哪一步出了问题。这是我最常用的调试手段比加一堆 echo 强得多。另一个强烈推荐的是 shellcheck 静态检查工具。它比人工 review 严格得多能发现未加引号的变量、权限设置问题、可移植性隐患。用完它很多新手代码的常见毛病一网打尽。安装方式各个发行版不同不多展开但很建议所有写脚本的人都装一个。最后分享一个真实案例有个备份脚本手动执行一切正常放到 crontab 里就报错。我排查了很久最后发现原因是脚本里用了相对路径人手动执行时所在目录恰好对脚本能找到配置文件cron 执行时工作目录不对自然就找不到文件了。这类问题在运维里反复出现解决办法有两个脚本内部统一使用绝对路径或者在脚本一开始用cd $(dirname $0)把自己切到脚本所在目录。两种方式我都用过从稳定角度我更喜欢第一种。学 Shell 这件事说到底不是背命令而是形成一种解决问题的思路。拿到一个任务先想清楚输入是什么、处理逻辑是什么、输出要什么然后用组合命令实现。AI 能帮你写出骨架但它的脚本必须经过你的审查才能进生产环境。我最后再分享一个小技巧多去翻/usr/bin下系统自带的脚本那些是无数人维护过的代码比任何教程都值得琢磨。在一个可靠的小环境里反复练习比收藏一百篇文章有用得多。
阅读完成 · 觉得有帮助?