ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Linux Bash脚本入门:从创建、运行到排错全流程解析

2026/9/30 1:46:11 拓冰建站 浏览量
Linux Bash脚本入门:从创建、运行到排错全流程解析 前阵子有个新同事问我Linux 下怎么把平时敲的命令存下来、一次性跑完。他说自己每次都一个个敲既慢又容易漏。这个问题我太熟了当年我也是从这儿入的门。所谓 Shell 脚本简单说就是把一串命令写进一个文件里然后交给解释器去执行而在绝大多数 Linux 发行版上这个解释器就是 Bash。今天这篇就围绕 Linux 下 Bash 脚本的创建、运行和排错把基础路径完整走一遍。文章会从环境准备、第一个脚本、核心语法、常见报错几个方面展开内容偏向动手实操适合刚接触 Linux 或者想把日常操作自动化的朋友参考。1. 先搞明白Shell 脚本到底解决什么问题很多人第一次接触 Shell 脚本时会有个误区觉得它是一门“编程语言”要去系统学一遍 syntax。其实不用这么想。Shell 脚本本质上就是你把命令行里能敲的指令按顺序写进一个文件再加点逻辑控制判断、循环、变量让系统自动去执行。1.1 Bash 在 Linux 系统里的位置Linux 系统启动后你打开终端敲命令那个“接收你的输入、解释并执行”的程序就是 Shell。Shell 有好几种常见的有 Bash、Zsh、Fish 等但 BashBourne Again Shell是绝大多数 Linux 发行版的默认 Shell也是我们今天的主角。你可以在终端里输入echo $SHELL查看当前默认 Shell大概率会输出/bin/bash。这行路径就是 Bash 解释器所在的位置。当你写好一个脚本文件想办法让系统知道“用哪个程序来解释这个文件”这就引出了脚本第一行非常重要的内容——Shebang。1.2 从“手动敲命令”到“自动化执行”想象一下你每天上班都要做这样一件事进入某个目录、备份数据库、清理旧日志、重启服务。这些命令你每天都要敲一遍不仅浪费时间还容易敲错。如果用脚本你只要把这些命令按顺序写进一个.sh文件之后每天运行一次这个文件就行了。再进一步你还可以让脚本接收参数比如“备份今天的日志”和“备份前天的日志”用同一个脚本传入不同日期就能完成。甚至可以用cron定时任务让脚本每天凌晨自动跑完全不用人管。这就是 Shell 脚本的核心价值把重复劳动变成一次性投入。1.3 什么场景适合用脚本不是所有任务都适合写脚本这点需要经验判断。我见过有人为了“显得专业”把一条mv命令也包一层脚本完全没必要。适合用脚本的场景通常具备这几个特征命令步骤多且执行顺序固定。需要反复执行尤其是定期执行。需要带参数执行比如指定不同的目录或文件名。需要做简单的判断比如“文件存在才执行”“上一条命令成功了才继续”。如果你只是偶尔手动敲一两条命令直接敲就行别给自己增加维护负担。但一旦你发现自己最近一周都在重复同一串命令那就该停下来写个脚本了。2. 动手前的准备工作编辑器、终端与权限写脚本不需要装什么复杂 IDELinux 下最简单也最通用的组合就是“一个文本编辑器 一个终端”。但正因为它看起来太简单很多新手会在细节上栽跟头比如文件没保存成 UTF-8、忘了给执行权限、换行符不对导致运行报错等。2.1 选一个趁手的编辑器图形界面下可以用 gedit、VS Code 之类的编辑器但服务器上通常没有图形界面。所以我建议从一开始就适应终端里的编辑器至少掌握两种里的一个nano和vim。nano上手极快打开文件、编辑、按CtrlO保存、CtrlX退出就这四个操作。适合刚开始写脚本、不想折腾的人。vim功能强大但学习曲线比较陡第一次打开可能连退出都不会。不过 vim 在远程服务器上几乎无处不在后续想深入 Linux 操作时这关迟早要过。我的建议是第一天先用 nano 写脚本把注意力放在脚本本身等写到自己觉得有需要时再花两周时间集中学 vim。2.2 脚本文件的命名与存放位置脚本文件名建议以.sh结尾比如backup.sh。虽然不强制但这样一眼就能看出是 Shell 脚本方便自己和同事识别。存放位置上个人建议先放在自己的家目录下建一个专门放脚本的文件夹比如~/scripts这样便于管理和备份。等你写了一些公共效率工具想让系统任何位置都能直接调用时再把脚本放或软链接到/usr/local/bin这种 PATH 目录下。注意不要在 Windows 下用记事本编辑脚本然后直接传到 Linux 上跑十有八九会报/bin/bash^M: bad interpreter错误后面第 5 节我会细说这个问题。2.3 关于“执行权限”这件事Linux 下要把一个文件当作程序来运行需要该文件有“可执行”权限。查看权限用ls -l 文件名你会看到类似-rw-r--r--的输出这表示文件主可读可写其他人只能读所有人都不可以执行。给脚本加可执行权限的命令是chmod x 你的脚本.sh这一步是 Linux 初学者的常见盲点。很多新手写完脚本直接./脚本.sh运行结果提示Permission denied第一反应是“是不是系统坏了”。其实只是忘了赋权。3. 创建并运行第一个脚本从 Hello World 开始前面做了那么多铺垫现在正式动手。下面的步骤我会写得很具体建议你照着操作一遍先把“能跑起来”的感觉找到后面再逐步叠加语法。3.1 第一步创建脚本文件先进入你准备好的目录然后新建一个文件。用 nano 的话命令是cd ~/scripts nano hello.shnano 会打开一个空文件。在文件里输入下面这几行#!/bin/bash # 这是我的第一个 Shell 脚本 echo Hello, World! echo 当前时间: $(date)第一行#!/bin/bash就是前面说的 Shebang它告诉系统用/bin/bash这个程序来解释执行本文件。第二行以#开头的是注释只给人看系统不会执行。第三、四行是实际要执行的命令echo用来输出内容$(date)表示先执行date命令再取它的输出结果。按CtrlO保存回车确认文件名再按CtrlX退出 nano。3.2 第二步赋予可执行权限回到终端执行chmod x hello.sh这时候你用ls -l hello.sh看文件权限应该变成-rwxr-xr-x多了几个x说明可执行权限已经加上了。3.3 第三步运行脚本运行脚本有几种方式这里先介绍最最常用的两种。方式一相对路径运行./hello.sh注意前面的./不能省略。这是很多新手会遇到问题的点为什么不直接输hello.sh因为当前目录默认不在 PATH 环境变量里直接输文件名系统会去 PATH 定义的几个目录里找找不到就报command not found。加了./就是明确告诉系统“在当前目录找”。方式二用 bash 解释器直接执行bash hello.sh这种方式不要求文件有可执行权限甚至不要求第一行有 Shebang因为你就是明确指定了用 bash 来解释它。在脚本调不通的时候暂时用bash跑一遍有助于缩小排查范围。如果一切正常你会看到三行输出。第一个小目标完成。3.4 几种运行方式的区别我之所以强调运行方式是因为它们在细节上确实不同运行方式是否需要执行权限是否依赖 Shebang适用场景./script.sh需要需要日常执行脚本bash script.sh不需要不需要调试、无执行权限时source script.sh不需要不需要在当前 Shell 中执行用于加载配置sh script.sh视情况视情况注意 sh 可能指向 dash行为有差异特别说一下source可以缩写为.。这种方式不是启动一个新的解释器进程来跑脚本而是在当前终端进程里直接执行。这意味着脚本里设置的变量、切换的目录会保留在当前终端里。如果你写一个脚本想临时切个目录、设置一些环境变量就用source跑如果你希望脚本在一个隔离环境里跑完就结束不污染当前终端就用./。这个区别很多搞了两年 Linux 的人都未必说得清楚。我建议你亲自试一下写个脚本cd /tmp然后分别用./和source运行再pwd看看当前目录有没有变化一下子就理解了。4. 脚本语法核心变量、条件、循环与函数第一个脚本跑通后你现在已经掌握了“把命令顺序放进去执行”的基本能力。但光会顺序执行脚本的价值还很有限。真正让脚本变得“智能”的是它能根据条件做决定、重复做某些事、接收外部输入、把重复块封装起来。这一节把这些核心语法一次讲透全都配可运行的例子。4.1 变量让脚本“记住”一些东西变量是脚本的基本存储单元命名规则是“字母、数字、下划线不能以数字开头”。定义和使用的写法如下#!/bin/bash # 变量定义等号两边不能有空格 name张三 today$(date %Y-%m-%d) echo 你好$name echo 今天是 $today这里有两个新手极易踩的坑赋值时name 张三这样加空格是错的。Shell 会把它解析成“执行 name 命令参数是 和 张三”然后报错。使用变量时加不加$区别很大。$name是取变量的值name只是一个普通字符串。变量还可以和其他字符串拼接写法稍微讲究一点#!/bin/bash prefixbackup suffix20250101 filename${prefix}_${suffix}.tar.gz echo $filename输出结果是backup_20250101.tar.gz。花括号{}的作用是把变量名边界标清楚避免解析歧义。比如echo $prefix_suffix会被当成一个名字为prefix_suffix的变量这就错了。4.2 位置参数与读取用户输入脚本还能接收外部传入的参数这在自动化任务里太常用了。比如你写一个备份脚本想把备份的目录名作为参数传进来而不是每次改脚本。位置参数用$1、$2表示第一个、第二个参数#!/bin/bash echo 第一个参数: $1 echo 第二个参数: $2 echo 参数个数: $# echo 所有参数: $保存为args.sh后执行./args.sh hello world linux输出对应就是hello、world、3、hello world linux。如果参数个数不够直接引用$2也不会报错只是拿到一个空值。需要交互输入时用read命令#!/bin/bash read -p 请输入要备份的目录: target_dir echo 你输入的目录是: $target_dir-p参数表示先输出一段提示文字然后等待用户输入把结果存到变量target_dir里。4.3 条件判断让脚本“分情况处理”条件判断让脚本不再是死板地顺序执行。最基本的写法是if ... then ... else ... fi注意fi是结束标记很多新手会漏掉。先看一个最简单的例子#!/bin/bash if [ $1 -gt 100 ]; then echo 参数大于 100 else echo 参数小于等于 100 fi保存后执行./check.sh 50会输出“参数小于等于 100”。这里的[ ... ]是test命令的简写-gt是大于greater than的意思。除此之外还有-eq等于equal-ne不等于not equal-lt小于less than-ge大于等于greater or equal-le小于等于less or equal字符串和文件判断也很常用#!/bin/bash if [ -e /etc/passwd ]; then echo 文件存在 fi if [ -d /etc ]; then echo 是目录 fi strabc if [ $str abc ]; then echo 字符串相等 fi-e判断文件是否存在-d判断是否为目录。注意字符串比较用的是单个数字比较才用-eq。新手很容易把这两者搞混。还有个细节变量加引号。在if [ $str abc ]里如果变量没值或值里带空格不加引号会出问题。建议所有变量引用都加双引号这是经验之谈。4.4 for 循环与 while 循环让重复工作自动化循环是脚本自动化中非常出彩的部分。比如你想批量把当前目录下所有.txt文件重命名为.bak手动做很麻烦用 for 循环几行搞定#!/bin/bash for file in *.txt; do mv $file ${file%.txt}.bak echo 已处理 $file done*.txt会被 Shell 扩展成当前目录下所有.txt文件列表然后逐个赋值给file变量循环体执行重命名操作。${file%.txt}是去掉结尾.txt的写法是一个字符串截取技巧。for 循环也可以直接遍历一段数字#!/bin/bash for i in {1..5}; do echo 第 $i 次循环 done还有个常见写法是 C 风格#!/bin/bash for ((i1; i5; i)); do echo 第 $i 次循环 donewhile 循环适合“当某个条件成立时一直跑”。最常见的用途是逐行读取文件#!/bin/bash while read line; do echo 读取到: $line done /etc/hostname符号把文件内容重定向到循环中read逐行读取并赋值给line变量。4.5 函数把重复逻辑抽出来当一个操作在脚本里反复出现三遍以上就应该考虑写成函数。函数定义的格式如下#!/bin/bash log_info() { echo [INFO] $(date %Y-%m-%d %H:%M:%S) - $1 } log_info 脚本启动 log_info 开始备份 log_info 备份完成这个例子里log_info把记录日志的格式统一封装成了函数每次调用只要传一段消息内容即可。函数体里的$1是函数自己的第一个参数跟脚本的$1不是一回事别混了。函数定义之后必须先定义后调用。脚本在跑的时候是一行一行解析的如果调用写在函数定义之前会报command not found。4.6 综合小案例自动备份脚本把上面几个内容串起来写一个稍完整的脚本。目标是传入一个目录路径检查目录是否存在存在则打包备份到~/backups目录备份文件名带当天日期并打印日志信息。#!/bin/bash # 自动备份脚本 v1.0 backup_dir$1 backup_root$HOME/backups log_info() { echo [INFO] $(date %Y-%m-%d %H:%M:%S) - $1 } if [ $# -lt 1 ]; then echo 用法: $0 要备份的目录 exit 1 fi if [ ! -d $backup_dir ]; then echo [ERROR] 目录不存在: $backup_dir exit 1 fi mkdir -p $backup_root filename${backup_root}/$(basename $backup_dir)_$(date %Y%m%d_%H%M%S).tar.gz tar -czf $filename $backup_dir log_info 备份完成: $filename你可以直接复制保存为backup.sh然后chmod x backup.sh再执行./backup.sh /etc试试效果。这个脚本里用到了参数判断、错误提示、变量拼接、函数定义、时间获取、打包命令算是第一阶段语法的小综合。5. 新手高频报错与排查技巧脚本报错是必经之路没有人能一次写对。我下面列的几个问题十个新手有九个会遇到提前把解决方法摆在这里真碰上了直接按图索骥。5.1/bin/bash^M: bad interpreter: No such file or directory这个报错信息几乎可以排到“Shell 脚本报错榜”第一名。问题根源在于文件是在 Windows 下编辑的Windows 每行结尾是\r\n回车 换行而 Linux 期望的是\n仅换行。在 Linux 里\r会被当成非法字符导致解释器路径都读不对。解决方法有两个方法一安装 dos2unix 工具转换。sudo apt install dos2unix # Debian/Ubuntu sudo yum install dos2unix # CentOS/RHEL dos2unix your_script.sh方法二用 sed 直接删除换行符前面的\rsed -i s/\r$// your_script.sh我个人推荐方法二因为sed在所有 Linux 系统上都有不用额外安装。改完之后再运行一遍问题就解决了。防范措施也很简单脚本文件统一在 Linux 上编辑或者编辑器里设置换行符为 LF。5.2command not found这个报错有两种完全不同的场景。场景一脚本文件本身没有执行权限你直接输脚本名不带./运行Shell 在 PATH 里找不到。解决方法是加./前缀或者用bash直接跑。场景二脚本内部执行某条命令时提示command not found。比如lsusb: command not found这是系统里压根没装这个命令对应的包需要先安装。又比如你用了一些非标准路径下的工具但脚本运行时 PATH 环境变量里没有包含它。排查思路很清晰先看是哪一行报错把那行命令单独拿到终端里执行看是否成功不成功就针对性解决。5.3Permission denied这个前面已经提到了脚本没有可执行权限。解决方法是chmod x script.sh如果你的脚本要给别人用可以思考一下权限范围chmod 755 script.sh表示文件主可读写执行他人可读可执行如果只是自己用chmod 700 script.sh更安全。5.4unexpected end of file或syntax error: unexpected end of file这个报错通常是因为if没有配fi或者for没有配done再或者花括号、双引号没配对。Shell 脚本是按行执行的一旦结构不完整解析器读到文件结尾也没找到对应的结束标记就会这样报。排查方法把一个脚本缩减到最小可复现粒度逐段注释掉代码找到出问题的区块检查对应的闭合成对符号。平时写脚本注意缩进——虽然 Shell 不像 Python 那样强制缩进但有缩进才能人眼发现结构不配对。5.5 参数带空格导致脚本行为异常如果调用脚本时传了个带空格的参数比如./backup.sh /home/user/my documents脚本里的$1只会拿到/home/user/my空格后面的部分被拆成了另一个参数。解决方法是调用时加引号./backup.sh /home/user/my documents同时脚本内部所有引用$1、$2的地方也建议加双引号养成习惯就不会被空格坑。5.6 常见问题速查表报错信息常见原因解决方法/bin/bash^M: bad interpreterWindows 换行符执行sed -i s/\r$// 脚本command not found权限/PATH/缺包加./或用 bash检查命令是否存在Permission denied未加执行权限执行chmod x 脚本unexpected end of file结构不配对检查 fi、done、括号、引号syntax error near unexpected token语法拼写错误检查 if 是否拼成 i、do 是否漏掉No such file or directory引用了不存在的路径检查路径名文件是否已创建6. 让调试变得更轻松的实用小技巧脚本写得多了难免要调试。这里分享几个我平时一直在用、能显著提高效率的调试习惯。6.1 用bash -x追踪执行过程在运行脚本时加上-x参数可以打印每一行实际执行的命令。这对排查“脚本跑完了但结果不对”的问题非常有效bash -x your_script.sh输出里每一条被执行的命令都会带上前缀显示在屏幕上你能清楚看到变量展开成了什么值、哪一行走到了哪一步。进阶一点还可以在脚本内部通过set -x开启追踪、set x关闭追踪只用set -x包裹需要重点观测的区间。6.2 善用echo打印关键状态新手调试最容易犯的错是不打日志、闷头跑。在关键节点加一行echo输出一下“现在执行到了哪里、某个变量的值是什么”能节省大量排查时间。比如echo DEBUG: backup_dir $backup_dir等确认逻辑没问题了再把调试用的echo删掉或者用#注释掉。这看起来笨但它是最直接有效的调试方式尤其适合初学者。6.3 保持脚本语法检查的好习惯正式运行之前可以先做一次语法检查不实际执行bash -n your_script.sh如果脚本有语法错误-n会直接报出来没有错误则什么都不输出。养成写完脚本先跑bash -n的习惯能挡住很多低级错误。再把bash -x结合使用一个查语法、一个查逻辑基本可以覆盖初期的调试需求。6.4 把“写脚本”当“搭积木”一个常见问题是新手总想一次写一个几十行的大脚本结果一运行全是错不知道从哪改起。我的经验是拆分先写一个最小功能的脚本跑通后再往里加功能。比如先只做“打包某个目录”通了再加“日志记录”再加“参数校验”再加“旧文件清理”。每加一个功能就运行一次如此能保证你永远知道问题出在最新加的那部分。6.5 脚本注释的重要性注释不参与执行但它是维护脚本生命力的关键。刚从教材里学语法的人总觉得注释是多余的。但等你三个月后回来看自己写的脚本面对一堆完全没有注释的命令很可能要花大量时间回忆当时为什么要这样写。建议每个脚本至少包含这几行注释#!/bin/bash # 脚本功能备份指定目录到 ~/backups # 用法./backup.sh 目录路径 # 最后修改2025-01-01涉及特殊逻辑的地方也值得补一句“为什么”而不是“做了什么”——因为“做了什么”看代码本身就能懂“为什么”往往是真正有价值的信息。7. 写在最后的建议从运行脚本到自己写脚本到这里你已经把 Bash 脚本的核心路径走了一遍理解它是什么、会用 nano 创建文件、掌握变量、条件、循环、函数这些基础语法、知道怎么处理常见报错。接下来真正拉开差距的是你有没有带着“解决实际问题”的目标去写。我的建议很具体把身边重复做的操作逐个列出来从最简单的开始每个都试着脚本化。比如一键清理某个目录下超过 7 天的临时文件。批量重命名一批图片文件。拉取最新代码并重新打包部署。检查磁盘空间低于阈值时发警告日志。刚开始写不出来没关系拆开去找每部分的解决方案拼起来就是你的脚本。每完成一个你对命令的熟悉度、对 Shell 逻辑的敏感度都会上一个大台阶。我现在回头看很多更高级的 Linux 技巧其实都是当初为了写好某个自动化脚本一点点倒逼着自己去查、去试、去记下来的。这也算是最朴素也最有效的学习路径了。