很多人第一次见到这行字,是在一个本该出现系统登录界面的清晨:屏幕全黑,只有几行白字,最上面那句就是Minimal BASH-like line editing is supported,下面跟着一句冷冰冰的grub rescue>。那一瞬间大多数人的第一反应是"我的 Linux 是不是废了",第二反应是"BASH 坏了怎么办"。先给个定心丸:系统盘里的数据九成九还在,坏掉的是引导程序 GRUB 的"启动地图",而不是你的操作系统本身。这台机器现在处于 GRUB 的降级急救模式,它把一套模仿 BASH 行编辑习惯的极简命令行丢给你,让你自己想办法把系统拉起来。这篇文章面向所有装过双系统、动过分区、做过磁盘克隆、被 Windows 更新折腾过的人,也面向想搞清楚 GRUB 引导链条到底怎么运转的人。我会先把这条提示背后的机制讲透,再给你两套能直接抄的修复流程:一套是现场手敲命令临时进系统,一套是用启动盘彻底重建引导,最后顺带把几个和 BASH 相关的高频报错一起排掉。
1. 先搞懂这条提示到底在说什么
1.1 GRUB 的两个"急救室":grub> 与 grub rescue>
要理解这行提示,得先知道正常开机时 GRUB 做了什么。以传统 BIOS 主板为例,固件读完硬盘第一个扇区的引导代码后,把控制权交给 GRUB 的第一阶段代码;第一阶段代码的目标只有一个——找到并加载第二阶段的核心镜像core.img,这个镜像通常被塞在分区表后面那段几十 KB 的空隙里。core.img里编译进去了一条关键信息:prefix,也就是"我的配置文件和各种模块放在哪个目录下",典型值形如(hd0,msdos1)/boot/grub。拿到 prefix 之后,GRUB 会去那个目录加载normal.mod,读取grub.cfg,然后给你画出那个熟悉的系统选择菜单。
整条链条里任何一环断了,GRUB 都会退回最后一层保底机制——加载内置的 rescue 模块。这个模块小得可怜,只认识ls、set、insmod、unset、lsmod、prefix这几个命令,功能就是"让我看看磁盘上有什么、把路径设对、再把完整模块拉进来"。它内置了一个极简的命令行解释器,支持 TAB 补全、方向键翻历史、光标移动这些行编辑动作,用起来很像 BASH,所以它才在提示里自称 "BASH-like line editing"。这句话不是报错内容,而是 GRUB 在自我介绍,告诉你"我现在只有个简版命令行,TAB 可以补全命令"。这是全网误读最多的一句话,很多人看到 BASH 就跑去修 bash 环境,方向从一开始就偏了。
区分两个急救室很重要,它们的提示符完全不一样,能做的事情也差得很远。
| 提示符 | 触发条件 | 可用命令 | 典型表现 |
|---|---|---|---|
grub> | normal.mod已加载,但grub.cfg缺失或内容为空 | 几乎全部命令,含linux、initrd、boot | 直接进命令行,没有菜单 |
grub rescue> | 找不到 prefix,normal.mod都没能加载 | 只有ls、set、insmod等少数几个 | 伴随Minimal BASH-like line editing is supported |
Linux 的bash | 系统已启动,进入用户态 | 全部 Shell 功能 | 提示符是$或# |
这两个提示符的修复路径前半段一样,后半段分叉:grub>下你可以直接linux加initrd启动内核,而grub rescue>下敲linux会直接回你一句 "Unknown command",必须先insmod normal把完整模块请进来。这个细节决定了后面第 2 章的两种打法。
1.2 为什么偏偏是"只剩一个命令行"
搞清楚了 GRUB 的工作方式,触发原因就很好归纳了——本质上是**"GRUB 找不到自己的配置目录"或者"配置目录里的东西打不开"**。前者是路径问题,后者是文件系统或文件损坏问题。常见的触发场景我按出现频率排个序。
第一类是分区表被改写。装 Windows 的时候,安装程序会理所当然地重写引导记录;Windows 的大版本更新偶尔也会"顺手"把引导顺序改掉,把自家的引导项顶到第一位。第二类是/boot或/boot/grub被删、被清空、被格式化,比如清理磁盘空间时手滑,或者跟着某些教程rm -rf敲快了。第三类是core.img里记录的 prefix 失效,具体来说就是分区编号变了——加了一块新硬盘、改了 BIOS 里的硬盘顺序、从 U 盘启动过一次、把 NVMe 系统盘从 M.2 槽位挪到另一个槽位,都可能让原来的(hd0,msdos1)变成(hd1,msdos1),GRUB 按老地址去找,自然扑空。
第四类是整盘克隆或者虚拟机迁移。用克隆工具把系统盘复制到新盘后,分区的 UUID 全变了,grub.cfg和/etc/fstab里还写着旧 UUID,GRUB 去找不存在的分区,直接掉进 rescue。第五类是文件系统本身出了问题,比如断电导致的 ext4 日志未回放,GRUB 自带的ext2.mod读不出来,ls (hd0,1)/会告诉你unknown filesystem。第六类是 UEFI 和 Legacy 两种启动方式混着用,比如原本是 Legacy 装的系统,你却在 UEFI 模式下启动,固件读到的引导信息对不上。
| 触发原因 | 现场特征 | 修复难度 |
|---|---|---|
| 分区表被 Windows 改写 | 上次正常,装完系统开不了机 | 低,重建引导即可 |
/boot/grub被删 | ls (hd0,x)/boot里没有 grub 目录 | 中,需要重装 GRUB |
| 磁盘编号/顺序变化 | 提示符出现前没有任何异常 | 低,改回顺序或重装 |
| 克隆盘 UUID 变化 | 手动引导能进系统,重启又挂 | 中,需要改 fstab 与 cfg |
| 文件系统损坏 | ls报unknown filesystem | 高,先修复文件系统 |
| UEFI/Legacy 混用 | 主板启动项里能看到两个同名设备 | 中,需要切回正确模式 |
2. 进系统前的急救:手敲命令把系统拉起来
2.1 用 ls 摸清楚磁盘和分区的编号
进入grub rescue>之后,先深呼吸,第一条命令永远是ls,不带任何参数:
grub rescue> ls (hd0) (hd0,msdos1) (hd0,msdos5) (hd1) (hd1,gpt1) (hd1,gpt2) (hd1,gpt3)看到的东西是"盘"和"分区"两个层级。(hd0)代表第一块盘本身,(hd0,msdos1)代表它上面的第一个 MBR 分区,(hd0,msdos5)是第一个逻辑分区(MBR 扩展分区里的逻辑分区从 5 开始编号,这是很多人会愣一下的地方)。GPT 分区表则显示成(hd1,gpt1)这种形式,编号是连续的。
接下来逐个分区探测内容,看哪个里面有系统:
grub rescue> ls (hd0,msdos1)/ grub rescue> ls (hd0,msdos5)/判断依据很简单:/boot/grub或者/grub目录出现在哪个分区里,那个分区就是你要的。如果/boot是独立分区,你会看到该分区根目录下直接有一个grub文件夹;如果/boot和根分区合在一起,你会看到boot文件夹,往里再钻一层才是grub。这个区别直接决定了后面 prefix 怎么写,也是新手翻车最集中的地方。
注意:如果
ls (hd0,1)/返回的是unknown filesystem而不是文件列表,说明 GRUB 读不出这个分区的文件系统。此时不要急着设 prefix,先把这块盘接到另一台机器上做一次fsck,或者用启动盘里的fsck.ext4 -y /dev/sda1修复,修完再回来。
2.2 set root 与 set prefix 的正确写法
确认了分区之后,设置两个变量:
grub rescue> set root=(hd0,msdos5) grub rescue> set prefix=(hd0,msdos5)/boot/grubroot是"我要在哪个分区上找东西",prefix是"GRUB 的模块和配置文件具体在什么路径"。prefix 指向的是包含grub文件夹的那个目录,而不是grub文件夹本身,这一点务必咬死。举个例子,如果你的分区布局是/dev/sda1挂载到/boot,那么 prefix 应该是(hd0,msdos1)/grub;如果/boot没有独立分区,直接是根分区(hd0,msdos5)下的目录,那 prefix 才是(hd0,msdos5)/boot/grub。写错一个层级,下一步insmod normal就会回你file not found,而这个报错信息几乎不会告诉你错在哪一级,只能自己回头核对。
设置完可以用set单独打出来看一眼当前状态,用ls配合验证一下路径是否存在:
grub rescue> set root=(hd0,msdos5) prefix=(hd0,msdos5)/boot/grub grub rescue> ls (hd0,msdos5)/boot/grub能列出normal.mod、grub.cfg、i386-pc这些东西,说明路径对了。
2.3 insmod normal 与 normal 的两条路径
路径确认无误后,加载完整模块并切换到正常模式:
grub rescue> insmod normal grub rescue> normal成功的话,屏幕会刷出系统选择菜单,你直接选第一项回车,系统就起来了。如果菜单没出现但提示符变成了grub>,说明模块加载成功但grub.cfg有问题——可能是文件被清空,也可能是里面引用的分区 UUID 全部失效。
在grub>提示符下,你可以手动把内核拉起来,这套命令在 rescue 模式下加载完 normal 之后同样适用:
grub> set root=(hd0,msdos5) grub> linux /boot/vmlinuz-5.15.0-91-generic root=/dev/sda5 ro grub> initrd /boot/initrd.img-5.15.0-91-generic grub> boot这里有两个关键细节,全是血泪教训换来的。第一,内核文件名不要靠记忆去敲,用 TAB 补全。输入/boot/vmlinuz之后按 TAB,GRUB 会把候选列出来,如果只有一个就自动补全,有多个就列成一行一行给你挑。第二,linux后面那个root=参数用的是内核认得的设备名,比如/dev/sda5、/dev/nvme0n1p2,不是 GRUB 的(hd0,msdos5)。这两个命名体系是独立的,混着写会得到VFS: Cannot open root device的经典报错。GRUB 的编号从 0 开始数磁盘、从 1 开始数分区;Linux 内核那边/dev/sda从 a 开始排、分区从 1 开始排,NVMe 设备还多一个p。见到(hd0,gpt2)就对应/dev/sda2或/dev/nvme0n1p2,具体是哪块要看ls的结果顺序。
grub> linux /boot/vmlinuz<TAB> grub> initrd /boot/initrd.img<TAB>提示:如果
insmod normal报unknown filesystem,说明该分区的文件系统模块缺失。可以试着先加载对应模块,例如insmod ext2(ext2/3/4 共用这一个模块),再执行insmod normal。这个顺序问题文档里基本不写,但现场很有用。
2.4 别急着欢呼,这只是临时的
手敲命令能进系统,说明硬盘数据完好,问题纯粹出在引导层。但要清醒地认识到:你在 rescue 模式里做的所有设置只存在内存里,一重启全没了。很多人看到系统起来了,重启一下又回到黑屏,然后误以为"修不好",其实是没做持久化。
进入系统后,第一件事是确认根分区的挂载情况和 GRUB 配置的位置:
lsblk -f cat /etc/fstab ls -l /boot/grub/grub.cfg mount | grep bootlsblk -f会告诉你每块盘、每个分区的文件系统类型、UUID 和挂载点,这个输出后面重建引导时还要用。cat /etc/fstab让你知道系统认为自己的根分区是哪个,如果这里的 UUID 和lsblk报出来的对不上,那问题就找到了——克隆盘导致的 UUID 错乱,需要把 fstab 里的 UUID 改成新的。
确认没问题之后,在能正常进系统的前提下,可以直接就地重装 GRUB,不必用启动盘:
sudo grub-install /dev/sda # Legacy BIOS 场景,参数是整块盘不是分区 sudo update-grub # Debian/Ubuntu 系;RHEL 系用 grub2-mkconfig -o /boot/grub2/grub.cfgUEFI 场景则要指明目标平台和 EFI 分区:
sudo grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=ubuntu --recheck sudo update-grubupdate-grub会扫描系统里所有的内核镜像和已存在的操作系统,重新生成grub.cfg。跑完之后重启验证一次,如果还回 rescue,说明问题不在配置生成,而在更底层的引导记录,那就得走第 3 章的启动盘流程。
3. 一次性修好:用启动盘重建 GRUB 引导
3.1 准备工作与分区识别
当系统已经完全进不去,或者手敲命令加载 normal 也失败时,最稳的办法是从启动盘进去修。准备一个 8GB 以上的 U 盘,用镜像写入工具把某个 Linux 发行版的安装镜像写进去,实体机推荐用 Rufus 或 balenaEtcher,命令行党用dd也可以。写入之前记得备份 U 盘里的东西,这个过程会清空整块盘。
插上 U 盘,开机时按主板对应按键进启动菜单(常见的是 F12、F11、Esc、F8,各家不同),选择 U 盘启动。进入安装界面后不要真的安装,选"试用"模式,进入一个完整的桌面环境,然后打开终端开始干活。
第一步永远是识别分区结构,别凭感觉:
lsblk -f sudo fdisk -l sudo blkidlsblk -f输出的FSTYPE和MOUNTPOINT列最有价值:标着fat32或vfat、大小几百 MB 的通常是 UEFI 的 EFI 系统分区;标着ext4且几百 MB 到 1GB 的,很可能是独立的/boot;容量最大、标着ext4或btrfs的,一般是根分区。如果你用的是 LVM 或者加密根分区,lsblk里会看到lvm类型,还需要先激活卷组,这一步后面单独说。
3.2 BIOS 与 UEFI 两套修复流程的差异
这两种引导方式的修复命令完全不同,先看对比表,再看具体操作。
| 对比项 | Legacy BIOS + MBR | UEFI + GPT |
|---|---|---|
| 引导代码位置 | 整块磁盘的第一个扇区(MBR) | EFI 系统分区里的.efi文件 |
| 需要挂载的分区 | 根分区(有独立/boot也要挂) | 根分区 + EFI 分区 |
| 安装命令目标 | grub-install /dev/sda | grub-install --target=x86_64-efi --efi-directory=... |
| 引导项管理 | 由主板固件自动识别 | efibootmgr管理启动顺序 |
| 常见坑 | 参数写成分区号导致失败 | EFI 分区没挂载或不是 FAT32 |
| 分区表容量限制 | 主分区最多 4 个 | 支持 128 个分区 |
判断自己属于哪种,可以在启动盘的终端里跑一句:
[ -d /sys/firmware/efi ] && echo "当前是 UEFI 模式" || echo "当前是 Legacy 模式"注意这句判断的是"你现在以什么模式启动",如果 U 盘是以 Legacy 方式启动的,而系统原本是 UEFI 安装的,这句会误报。更可靠的办法是看硬盘分区表类型:sudo fdisk -l里显示Disklabel type: gpt且存在 EFI 分区,基本就是 UEFI 系统。
3.3 chroot 修复的完整步骤拆解
修复的核心思路是:把硬盘上的系统"临时接管"到启动盘环境里,让grub-install以为自己运行在正常系统中。这就是 chroot。每一步都有讲究,我按顺序写清楚。
# 1. 挂载根分区(按你自己的设备名替换) sudo mount /dev/nvme0n1p2 /mnt # 2. 如果 /boot 是独立分区,先挂它 sudo mount /dev/nvme0n1p3 /mnt/boot # 3. 如果是 UEFI,挂载 EFI 分区 sudo mount /dev/nvme0n1p1 /mnt/boot/efi挂载顺序不能乱:必须先挂根分区,再挂子目录,否则子分区会被根分区覆盖掉变成空目录,这个坑我踩过一次,现象是 chroot 进去之后/boot里什么都没有,还以为是分区坏了。
# 4. 把系统运行所需的关键目录绑定进去 sudo mount --bind /dev /mnt/dev sudo mount --bind /dev/pts /mnt/dev/pts sudo mount --bind /proc /mnt/proc sudo mount --bind /sys /mnt/sys # 5. 如果修复过程需要联网安装包,把 DNS 配置带进去 sudo cp /etc/resolv.conf /mnt/etc/resolv.conf # 6. 切换根目录 sudo chroot /mnt /bin/bash--bind这几步看起来多余,实际很关键。/dev不给,grub-install找不到磁盘设备;/proc和/sys不给,一些工具探测硬件信息时会报奇怪的错误。省略它们有时候也能跑通,但一旦出错,报错信息往往误导性极强,排查成本远高于多敲四行命令。
进入 chroot 之后,先验证身份:
ls / cat /etc/os-release能看到完整的根目录结构和发行版信息,说明接管成功。接下来重建引导:
# UEFI 系统 grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=ubuntu --recheck # Legacy 系统 grub-install --target=i386-pc --recheck /dev/nvme0n1 # 重新生成配置文件 update-grub # 或者通用写法 grub-mkconfig -o /boot/grub/grub.cfg几个参数的含义值得说透。--recheck让 grub-install 重新探测设备映射,避免使用可能过期的缓存;--bootloader-id是写进 EFI 分区和固件启动项里的名字,随便叫什么都行,但如果你希望和原来的保持一致(比如原来的叫 ubuntu),就照原样写;--efi-directory必须指向已挂载的EFI 分区挂载点,没挂载就会报failed to get canonical path of /boot/efi,这个报错的字面意思和真实原因之间隔了十万八千里,很多人在网上翻半天找不到答案。
收尾工作同样不能省:
exit # 退出 chroot sudo umount -R /mnt # 递归卸载,一次搞定所有挂载点 sudo reboot注意:
umount -R的顺序是从内到外自动处理的,比手动一条条卸载靠谱得多。如果卸载时报target is busy,先cd /离开当前目录,再用sudo fuser -m /mnt看看是谁占着。
3.4 验证引导项与启动顺序
UEFI 系统重启前,最好在启动盘环境里确认一下引导项写对了:
sudo efibootmgr -v输出会列出所有固件启动项,形如Boot0001* ubuntu HD(1,GPT,...)/\EFI\ubuntu\grubx64.efi。如果这里的路径是错的,或者你的系统项被排到了后面,可以调整顺序:
sudo efibootmgr -o 0001,0000,0002这条命令把启动顺序重排成 0001 优先。如果efibootmgr里根本找不到你的系统项,说明grub-install的 NVRAM 写入没成功,可能是主板固件的问题,可以再进一次 chroot 重跑一遍,或者用--no-nvram参数跳过写入、手动在固件设置界面里添加引导文件路径。
重启后如果进了系统,还有几件事值得做:检查/etc/fstab里的 UUID 和lsblk -f是否一致;跑一次sudo grub-install对应命令确认无误;把这次用到的命令记到自己的笔记里。下次真出事的时候,翻笔记比翻搜索引擎快得多。
4. 修完还会复发?几个容易被忽略的坑
4.1 Windows 更新和系统重装导致的引导被顶掉
Windows 在引导顺序上相当霸道,安装过程和某些大版本更新都会把自家的引导项顶到第一位,甚至直接覆盖 MBR 里的引导代码。如果你用的是双系统,正确的做法是先装 Windows 再装 Linux,这样后装的 GRUB 才能接管引导并自动识别 Windows 分区。顺序反了的话,每次 Windows 更新都可能让你重新体验一遍黑屏。
如果已经被顶掉了,不用慌,进 Linux 后跑一次sudo update-grub,它会自动扫描到 Windows 的引导文件并加进菜单。如果连 Linux 都进不去了,就按第 3 章的流程用启动盘修,修完记得在固件设置里把 Linux 的引导项提到前面,很多主板的启动菜单里可以直接拖拽排序。
4.2 磁盘克隆后 UUID 变了,手动引导能进但重启就挂
这个现象很有迷惑性:手敲命令能进系统,说明引导文件和内核都在;但一重启就回 rescue,说明系统自己生成的配置指向了错误位置。根源在 UUID。克隆之后,新分区的 UUID 和旧盘完全不同,而/etc/fstab和/boot/grub/grub.cfg里还写着旧的那串字符。
修复思路有两条。第一条是"迁就系统",把 fstab 和 grub.cfg 里的 UUID 改成新值:
sudo blkid # 查出新的 UUID sudo nano /etc/fstab # 逐个替换 sudo update-grub # 用新信息重新生成 cfg第二条是"迁就配置",把新分区的 UUID 改回旧的,这样连 fstab 都不用动:
sudo tune2fs -U <原来的UUID> /dev/sda2第二条更省事,但前提是你还留着旧 UUID 的记录,而且新旧分区不能同时在线上(UUID 重复会导致挂载混乱)。个人建议还是走第一条,把配置改对,一次做干净。
4.3 多硬盘环境下 GRUB 的盘符不是固定的
(hd0)的具体含义会变。它取决于固件把哪块盘排在了最前面,而这件事在 BIOS 里可以手动调,也可能因为插了一块新的移动硬盘而改变。同样的道理,SATA 接口换了位置、NVMe 硬盘插到另一个 M.2 槽,都可能让系统盘从hd0变成hd1。
这里有个实用的对照表,现场排查时能省不少时间:
| Linux 设备名 | GRUB 写法 | 说明 |
|---|---|---|
/dev/sda | (hd0) | 第一块 SATA/SCSI 盘 |
/dev/sda1 | (hd0,msdos1) | MBR 第一个主分区 |
/dev/sda5 | (hd0,msdos5) | MBR 第一个逻辑分区 |
/dev/sda2 | (hd0,gpt2) | GPT 第二分区 |
/dev/nvme0n1p2 | (hd0,gpt2) | 前提是这块 NVMe 排第一 |
/dev/sdb1 | (hd1,msdos1) | 第二块盘 |
要彻底避免这类问题,可以在系统装上之后就把引导项写入 NVRAM(UEFI 场景)或者确保grub-install时指定的是正确的整块磁盘。更稳妥的做法是养成习惯:每次动过硬盘硬件配置之后,先开机验证一次,出问题的是当场就能定位,比过几个月突然黑屏再排查容易得多。
4.4 LVM 与加密根分区的额外步骤
如果根分区在 LVM 上,chroot 之前需要先激活卷组:
sudo vgscan sudo vgchange -ay ls /dev/mapper/看到类似vg-root、vg-swap的设备后,挂载/dev/mapper/vg-root而不是物理分区。如果根分区是加密的,还需要先解密:
sudo cryptsetup luksOpen /dev/nvme0n1p3 cryptroot然后挂载/dev/mapper/cryptroot。这层解密的顺序不能颠倒,否则你会看到一堆"设备不存在"的报错。加密系统修引导的完整流程比普通系统长一倍,操作前务必确认自己知道所有密码,中途卡在密码提示上是很尴尬的事。
5. 顺手排掉几个和 BASH 有关的高频报错
5.1 -bash: crontab: command not found 到底缺了什么
这个报错的字面意思很直白:找不到crontab这个命令。但它背后的原因有三种,得分开处理。
第一种是真没装。最小化安装的系统往往不带计划任务组件。Debian/Ubuntu 系装sudo apt install cron,RHEL/CentOS/Fedora 系装sudo dnf install cronie或sudo yum install cronie。装完之后记得确认服务在跑:systemctl status crond,没启动就systemctl enable --now crond。
第二种是装了但PATH里没有。用非交互式 shell 执行脚本时,环境变量可能被精简,PATH里只有/usr/bin:/bin,而crontab在/usr/bin/crontab的话本该能找到,但如果装在/sbin或其他位置就会失联。排查办法是先command -v crontab(比which更可靠,因为它认 shell 函数和别名),再用type -a crontab看所有候选。如果command -v有输出但直接敲没有,那就是别名或者PATH顺序的问题。
第三种是权限问题。普通用户执行crontab -l看自己的任务清单没问题,但-u指定其他用户就需要 root 权限。如果报的是权限相关的提示,加上sudo再试。
实操心得:把
command -v当作排查"命令找不到"的第一条命令。它比which更符合 POSIX 规范,能识别 shell 函数、别名和内置命令,输出也更干净。我现在的习惯是任何脚本报"command not found",先在同一个 shell 里敲一遍command -v xxx,能省掉大量猜测。
5.2 /bin/bash^m: bad interpreter 是换行符在捣乱
这个报错里那个^m特别扎眼,它是回车符\r的可视化表示。文件在 Windows 下编辑保存时,每一行结尾是\r\n两个字符;而 Linux 只认\n一个。于是内核在解析 shebang 那一行时,读到的实际内容是/bin/bash\r,它去找一个名叫bash\r的解释器,当然找不到,于是报出这个错。
确认方法很简单:
head -1 deploy.sh | cat -A如果输出是#!/bin/bash^M$,那个^M就是元凶,$表示行尾。
修复有三种,按场景选:
# 方案一:就地替换,最通用 sed -i 's/\r$//' deploy.sh # 方案二:用专用工具,适合批量处理 sudo apt install dos2unix dos2unix deploy.sh # 方案三:用 tr 生成新文件,不改原文件 tr -d '\r' < deploy.sh > deploy_fixed.sh chmod +x deploy_fixed.sh如果你用 vim,也可以在文件里执行:set ff=unix再:wq,:set ff会告诉你当前是dos还是unix,这个命令我用了很多年,比背参数省事。
根本性的预防是在 Windows 上配置 Git 的换行符策略。装 Git for Windows 时那个"Checkout Windows-style, commit Unix-style line endings"的选项对应的就是core.autocrlf=true,它在检出时把\n转成\r\n、提交时转回来,很适合纯 Windows 开发环境。但如果你经常把脚本传到 Linux 服务器跑,更推荐设成core.autocrlf=input,只在提交时转换,检出时保持原样:
git config --global core.autocrlf input项目根目录放一个.gitattributes文件更彻底,可以按文件类型精细控制:
*.sh text eol=lf *.bat text eol=crlf *.png binary这样无论团队成员用什么系统,脚本文件在仓库里永远存成 LF,服务器拉下来直接能跑。
5.3 Git Bash 安装时值得留意的几个选项
在 Windows 上想要一个顺手的命令行环境,Git Bash 是很多人的第一选择。安装过程本身不难,但几个选项选错了会长期影响体验。
下载安装包后,一路下一步,遇到选择默认编辑器时,如果不熟悉 vim,选 Notepad++ 或 VS Code 会让后续改配置文件舒服很多。遇到调整PATH环境变量的那一步,三个选项对应三种策略:只从 Git Bash 用、从命令行和第三方软件用、覆盖 Windows 自带的find和sort。推荐选中间那个,既能在 CMD 和 PowerShell 里直接敲git,又不会用 Unix 工具覆盖掉系统命令造成别的脚本出错。
换行符那一页就是上一节说的core.autocrlf,按你的实际使用场景选。终端模拟器那一页选 MinTTY,它支持窗口缩放、复制粘贴快捷键更符合习惯。最后一项关于git pull默认行为的,选默认或者 rebase 都行,团队有规范就按规范来。
装完之后有一个细节值得单独提:MinTTY 环境下运行需要交互的程序(比如 Python 的交互式解释器、某些需要读取键盘输入的命令)时,可能会遇到按键没反应的问题。这是因为 MinTTY 不是真正的 Windows 控制台。解决办法是给命令前面加winpty:
winpty python winpty mysql -u root -p新版本的 Git for Windows 已经在部分场景下自动处理了这个转换,但遇到交互异常时,加winpty依然是最快的验证手段。
5.4 BASH 行编辑快捷键:把命令行用成编辑器
回到最初那个话题——"BASH-like line editing",GRUB 模仿的其实正是 GNU Readline 那套行编辑能力。这套快捷键值得系统性地记住,因为它是通用的:bash、Python 交互式解释器、MySQL 客户端、很多 REPL 环境都支持。
| 快捷键 | 作用 |
|---|---|
Ctrl + A | 光标跳到行首 |
Ctrl + E | 光标跳到行尾 |
Ctrl + U | 删除光标之前的所有内容 |
Ctrl + K | 删除光标之后的所有内容 |
Ctrl + W | 删除光标前的一个单词 |
Ctrl + Y | 粘贴上一次被删掉的内容 |
Ctrl + R | 反向搜索历史命令,连按可继续往前找 |
Ctrl + L | 清屏,内容还在,只是滚上去了 |
Alt + B/Alt + F | 按单词向前/向后移动光标 |
Tab/Tab Tab | 补全;连按两次列出所有候选 |
最值得练熟的是Ctrl + R。敲到一半发现命令输错了,与其按几十次退格,不如直接Ctrl + R输入关键词,历史里匹配的命令会浮出来,按回车直接执行。另一个高频组合是Ctrl + A加Ctrl + K,一键清空整行,比长按退格快得多。
如果想要更个性化的行为,比如让补全忽略大小写、给常用命令加快捷键,可以写~/.inputrc:
set completion-ignore-case on set show-all-if-ambiguous on "\C-p": history-search-backward "\C-n": history-search-forward第一行让cd down也能补全Downloads;第二行让第一次按 TAB 就列出所有候选,而不是等第二次;后两行把Ctrl + P和Ctrl + N改成"按已输入前缀搜索历史",比默认的上一条下一条更精准。改完执行bind -f ~/.inputrc立即生效,不用重开终端。
习惯 vi 键位的人还可以在.bashrc里加一行set -o vi,之后按 Esc 进入命令模式,可以用h、l、w、b、dw这些操作来编辑命令行。用惯了 vim 的人会对这套键位爱不释手,但代价是在其他不支持 vi 模式的 shell 里会条件反射地按错键。
6. 常见问题速查表与我的实操心得
6.1 GRUB 故障速查表
| 现象 | 最可能的原因 | 排查动作 | 处理方式 |
|---|---|---|---|
grub rescue>+ BASH-like 提示 | prefix 失效,normal.mod 未加载 | ls逐个分区探测 | 设 root/prefix 后insmod normal |
grub>但没有菜单 | grub.cfg 丢失或为空 | ls (root)/boot/grub | 手动 linux/initrd 启动后重建 cfg |
insmod normal报 file not found | prefix 层级写错 | ls确认路径里有 normal.mod | 修正 prefix,注意 /boot 是否独立 |
ls报 unknown filesystem | 文件系统损坏或缺模块 | 用启动盘跑fsck | 修复文件系统或insmod ext2 |
| 手动引导能进,重启又挂 | 配置里的 UUID 是旧的 | 对比blkid与 fstab | 更新 fstab 并update-grub |
| chroot 后 grub-install 报 canonical path | EFI 分区没挂载 | mount看挂载点 | 挂载 EFI 分区后重试 |
| 装完系统找不到启动项 | NVRAM 未写入 | efibootmgr -v | 重装或用--no-nvram后手动添加 |
6.2 我在实际操作中积累的几条经验
第一条,动硬盘之前先备份/boot/grub/grub.cfg和/etc/fstab。这两份文件加起来不到 10KB,用 U 盘拷一下或者传到自己的笔记里都行。但它们在故障时价值极高,因为里面有正确的 UUID 和分区路径,能让你五分钟定位问题,而不是花两小时猜测。我现在给任何一台机器做分区调整前,都会先把这两份文件留个副本。
第二条,修 GRUB 的时候,别在 rescue 模式里折腾太久。手敲命令能进的系统,说明问题在引导层;手敲命令进不去的,说明问题更深,继续在 rescue 里试各种组合技纯属浪费时间。判断标准很简单:如果你已经试了insmod normal和手动加载内核两条路,还是进不去,果断插启动盘。启动盘流程虽然步骤多,但每一步都是确定的,比在黑屏上盲猜快得多。
第三条,update-grub不是万能药。它只负责重新生成配置文件,不会修复损坏的引导记录,也不会修复 UUID 错乱。如果主板固件根本没找到引导项,跑一百遍update-grub也没用,必须走grub-install。这两个命令的关系我打个比方:grub-install是把"钥匙"插到锁孔里,update-grub是告诉钥匙"这把锁对应的是哪扇门"。钥匙没插上,写再多门牌号也没意义。
第四条,虚拟机里复现一遍再动手。如果你对 GRUB 修复完全没经验,可以在虚拟机里装一个系统、手动删掉/boot/grub目录、重启进入 rescue,然后照着本文的步骤练一遍。这个过程花不了半小时,但能在真实故障来临时给你极强的信心。我自己第一次遇到这个问题时是拿一台不重要的旧笔记本练的手,第二次遇到时已经能闭着眼睛敲了。
第五条,关于换行符那类问题,养成在服务器上只用 LF 的习惯。我现在的做法是本地开发时就把编辑器配成"保存 shell 脚本用 LF",Git 里放.gitattributes兜底,服务器上再也不用为bad interpreter浪费时间。这种问题最讨厌的地方在于它不常出现,所以每次出现都要重新搜索一遍答案,把它一次性解决掉收益极高。
最后一点小提醒,如果你经常需要重装系统或者调整分区,可以考虑把/boot做成独立分区。这样做的好处是重建引导时路径明确、不容易和其他系统混淆,出问题时定位也更快。代价是要多规划一次分区大小,一般给 1GB 到 2GB 足够,装多个内核版本也不至于撑爆。这个取舍没有标准答案,取决于你动硬盘的频率,但至少值得在装系统前想一下。