1.Linux基础指令上【由浅入深-Linux】
文章目录
- 第一章: Linux 背景
- 第二章:Linux下基本指令
-
- `ls ` 指令详解 (List Directory Contents)
- 补充:xshell如何连接云服务器
- 补充:参数合并
- 补充:Linux下目录/文件/文件夹是什么?
- 补充:linux文件类型
- 补充:Linux路径的认识
- 补充:根目录和家目录
- `tree` 指令
- **`pwd`** 指令详解
- `cd`指令
- `clear` 指令
- `history` 指令
- `whoami` 指令
- `touch` 指令
- 补充:touch创建位置
- `rm`指令
- `mkdir`指令
- `rmdir`指令
- `man` 指令
- 补充:路径最后一个文件是目录文件可以在整个路径结尾处加/代表最后一个文件是目录?
- `cp` 指令
- `mv`指令
- `cat`指令
- `tac`指令
- `echo`指令
- 补充:重定向
- 补充:管道`|`
- `more`指令
- `less` 指令
- `head`指令
- `tail` 指令
- `date` 指令
- **`cal`** 指令
- **`find`** 指令
- 补充: `find` 命令中的 `-o` 操作符详解
- 补充:深入解析 Linux 的通配符与正则表达式
- **`which`** 指令
- 补充:命令其实是shell外壳?
- 补充:什么是bash
- 补充:Linux 指令全都是“可执行文件”吗?
- 补充:自己写的程序为什么要有`./`才能跑?
- `whereis`指令
- `alias`(别名)指令。
- 补充: Linux 命令行的“流程控制符”。
- 补充:Linux 的注释符号
- `grep` 指令
- `top` 指令
- `htop`指令
- `zip` 和 `unzip`
- 补充:SSH
- 补充:`rz/sz`
- 补充:xshell是终端?什么是终端?
- 补充:`scp`-Secure Copy(安全拷贝)
- `tar`指令
- 补充:tar指令只能使用相对路径?zip呢?为什么?
- `bc`指令
- `uname -r`指令
- `type`
- linux中的热键
- 第三章:shell命令以及运⾏原理
第一章: Linux 背景
1.linux诞生与发展的历史脉络
第一阶段:思想与技术的源头 —— UNIX 的诞生 (1960s - 1980s)
Linux 的核心设计哲学深深地根植于 UNIX 操作系统。
- Multics 项目的失败与启发:1960年代中期,贝尔实验室(Bell Labs)、麻省理工学院(MIT)和通用电气(GE)合作开发一个名为 Multics 的庞大操作系统。但由于目标过于宏大、系统过于复杂,项目进展缓慢,贝尔实验室最终退出了该项目。
- UNIX 的诞生 (1969):贝尔实验室的科学家 Ken Thompson 和 Dennis Ritchie 为了能继续玩一个叫“星际旅行”的游戏,决定自己在一个废弃的 PDP-7 小型机上写一个简化版的操作系统。这个系统后来被称为 UNIX。
- C 语言的重写:1973年,Dennis Ritchie 发明了 C 语言,并将 UNIX 用 C 语言重新编写。这使得 UNIX 具有了极强的可移植性,迅速在高校和研究机构中传播开来。
- 走向封闭与昂贵:到了 1980 年代,AT&T(贝尔实验室的母公司)意识到了 UNIX 的商业价值,开始将其闭源并收取高昂的授权费。各大科技公司(如 IBM、HP、Sun)也纷纷推出自己专属且互不兼容的 UNIX 变种(如 AIX, HP-UX, Solaris)。UNIX 从一个极客共享的乐园变成了昂贵的商业产品。
第二阶段:精神支柱 —— GNU 计划与自由软件运动 (1983)
面对软件日益封闭和商业化,一位名叫 Richard Stallman(理查德·斯托曼) 的麻省理工学院黑客感到极其愤怒和痛心。
- 发起 GNU 计划 (1983):Stallman 发起了 GNU (GNU’s Not Unix) 计划。他的目标是建立一个完全自由、开放源代码的、与 UNIX 兼容的操作系统。
- 建立 FSF 与 GPL 协议:他创立了自由软件基金会 (FSF),并起草了著名的 GPL (GNU通用公共许可证)。GPL 的核心精神是:你可以自由地使用、修改和分发代码;但更准确地说,当你对外分发受 GPL 约束的衍生作品时,需要继续遵守 GPL 对相应源代码开放等方面的要求,并不是“只要自己修改了代码就必须立即公开”。
- “万事俱备,只欠东风”:到了 1990 年代初,GNU 计划已经成功开发出了大量优秀的系统工具(如 GCC 编译器、Emacs 编辑器、Bash Shell 等),但唯独缺少一个最核心的部件——操作系统内核 (Kernel)。GNU 自己的内核项目(Hurd)因为设计过于复杂,迟迟未能完工。
第三阶段:技术跳板 —— MINIX 操作系统 (1987)
为了在大学里教授操作系统原理,荷兰阿姆斯特丹自由大学的计算机教授 Andrew S. Tanenbaum(塔能鲍姆) 开发了一个微型、类 UNIX 的操作系统,命名为 MINIX。
- MINIX 的源代码包含在教授的教科书中,这让全世界的学生第一次能读到操作系统的底层代码。
- 但是,Tanenbaum 教授坚持 MINIX 只能用于教学目的,拒绝加入很多复杂的商业功能,并且它的许可协议在当时并不完全自由(不能随意修改和分发)。
第四阶段:历史的转折点 —— Linux 的诞生 (1991)
这个时候,历史的主角登场了。芬兰赫尔辛基大学的一名 21 岁计算机系学生 Linus Torvalds(林纳斯·托瓦兹),对 MINIX 的功能限制感到不满。
- 为了上网而写代码:Linus 最初只是想写一个终端仿真器,以便能用他的 386 电脑连接到学校的 Unix 服务器上网。后来,他发现自己不知不觉中已经写出了一个操作系统的雏形。
- 著名的“邮件宣言” (1991年8月25日):Linus 在 comp.os.minix 新闻组上发了一封著名的邮件:
“Hello everybody out there using minix - I’m doing a (free) operating system (just a hobby, won’t be big and professional like gnu) for 386(486) AT clones…” > (大家好,我正在开发一个自由的操作系统,只是个爱好,不会像 GNU 那么大那么专业…)
- 0.01 与 0.02 版:1991年9月,Linus 将 Linux 0.01 的源代码放到了 FTP 站点上,但 0.01 并不是一个真正面向普通用户正式发布、可直接使用的版本;1991年10月5日公开发布的 0.02 版已经可以运行 Bash 和 GCC。
第五阶段:GNU 与 Linux 的天作之合 (1992 及以后)
Linus 起初只写了内核。一个完整的操作系统除了内核,还需要各种外围软件和工具。
- 强强联合 (GNU/Linux):Linux 内核与 GNU 计划之前开发的大量优秀工具(如 GCC、Bash)结合在一起后,形成了可实际使用的完整系统。GNU 终于有了可配合使用的内核,Linux 也拥有了丰富的系统软件。严格从技术概念上说,Linux 首先指 Linux 内核;日常所说的“Linux 系统”通常指基于 Linux 内核构建的完整操作系统。对于大量采用 GNU 用户空间工具的系统,也常见 GNU/Linux 这一称呼。
- 拥抱 GPL 协议 (1992):Linus 最初使用的是自己写的一个禁止商业使用的许可协议。但在 1992 年发布的 0.12 版本中,他做出了一个影响深远的决定:将 Linux 重新以 GNU GPL 协议授权。
- 这个决定使得全球的顶级黑客和企业都可以放心大胆地参与到 Linux 的开发中,因为 GPL 保证了他们的代码永远不会被某一家公司私吞。

- 这个决定使得全球的顶级黑客和企业都可以放心大胆地参与到 Linux 的开发中,因为 GPL 保证了他们的代码永远不会被某一家公司私吞。
总结:为什么 Linux 能取得今天的成就?
Linux 从一个大学生的业余爱好,发展成为今天统治全球服务器、超级计算机、智能手机(Android 系统的底层正是 Linux)以及无数嵌入式设备的巨无霸,其背景原因可以归结为三点:
- 巨人的肩膀:它继承了 UNIX 优秀的架构思想和 POSIX 标准,这使得大量原有的 UNIX 程序员和软件可以零成本迁移。
- 时机与开源:它填补了当时“GNU有工具无内核”和“商业 UNIX 昂贵封闭”之间的空白。GPL 协议激发了人类历史上最大规模的分布式协作。
- 实用主义哲学:相比于 GNU Hurd 追求学术上的完美(微内核架构),Linus 采用了更务实、更容易出成果的宏内核(Monolithic kernel)设计,并在开发中始终坚持“能跑就行、快速迭代”的黑客精神。
在当今的企业级IT架构中,Linux 已经从早期的“极客玩具”蜕变为不可或缺的基础底座。绝大多数企业的核心业务、云平台以及底层硬件设施,都运行在 Linux 系统之上。
2. Linux 企业应用现状
目前,Linux 在企业中的应用已经渗透到从底层硬件到上层智能应用的各个环节,呈现出以下几个核心现状:
1) 支撑高性能与核心底层业务
在金融核心交易系统、大型数据库、高性能网关等要求极高吞吐量和极低延迟的场景中,Linux 是绝对的主力。在这些环境中,开发者通常会利用现代 C++ 标准(如 C++11 至 C++20)进行后端开发。为了榨取硬件的极限性能,开发者需要依托 Linux 环境,深入挖掘标准模板库(STL)的底层容器特性,并对内存管理机制进行极其精确的控制。
2) 嵌入式与硬件协同开发的枢纽
Linux 并非只运行在大型服务器上。经过内核裁剪后的 Linux 广泛应用于工业控制、路由器、智能车载系统等嵌入式领域。在很多复杂的硬件工程中,通常会采用“Linux 上位机 + 微控制器下位机”的架构。Linux 负责处理复杂的网络通信和数据计算,并通过串口、SPI 等总线与底层的微控制器(如 STM32)进行实时协同,形成完整的硬件控制闭环。
3) 构建复杂网络拓扑的基石
企业级的软件定义网络、虚拟专用网以及大规模路由交换架构,其底层多依赖于 Linux 内核的网络协议栈。在规划和搭建复杂的企业网络拓扑时,Linux 服务器常被配置为核心路由器、防火墙或负载均衡器,承载着极其庞大的数据包转发和流量清洗任务。
4) 云计算与人工智能的绝对承载者
无论是公有云提供商(如阿里云、腾讯云)还是企业内部的私有云,其底层的虚拟机管理程序和容器编排系统(如 Kubernetes)几乎全部基于 Linux。此外,当前热门的大型语言模型训练节点、以及各类 AI 智能体平台的算力集群,也均是在 Linux 环境下进行部署和任务调度的。
3. 企业级 Linux 主要发行版本
Linux 内核本身只是一个核心程序,不同的组织和商业公司将其与各种系统工具、桌面环境和应用软件打包,形成了不同的“发行版本”。 企业通常会根据稳定性、技术支持和生态体系来选择合适的版本:

1) 商业稳定派:红帽生态 (Red Hat 系)
这是传统企业服务器领域占有率最高的系列,以极度的稳定性和超长的生命周期支持著称。
- RHEL (Red Hat Enterprise Linux):由红帽公司提供的商业付费版本,提供全球顶级的企业级技术支持,主要用于金融、电信等对稳定性要求极高的核心生产环境。
- CentOS / CentOS Stream:曾经是 RHEL 最完美的免费源代码重新编译版本。目前已被红帽官方转型为 CentOS Stream(作为 RHEL 的上游测试版)。
- Rocky Linux / AlmaLinux:由于 CentOS 的转型,开源社区为了填补“免费企业级稳定版”的空白,重新推出了与 RHEL 完全兼容的这两个版本,目前正在被大量企业接纳为底层服务器系统。
2) 云原生与开发友好派:Debian / Ubuntu 系
这个系列以庞大的软件仓库、便捷的包管理和对新技术的快速响应而闻名。
- Ubuntu Server:目前在公有云以及开发者群体中最受欢迎的发行版。它拥有极其丰富的开源软件生态,特别是在 C++ 现代开发工具链、各类网络仿真工具以及深度学习框架的适配上,通常是各大开源项目的首选支持平台。
- Debian:Ubuntu 的上游母体。以纯粹的开源精神和坚如磐石的稳定性著称,系统非常干净且占用资源少,很多老牌系统管理员和追求极致精简的企业环境非常偏爱它。
3) 欧洲企业之选:SUSE 系
- SLES (SUSE Linux Enterprise Server):在欧洲市场占有率极高,最大的特点是与 SAP 等大型企业级资源规划软件的结合极其紧密,是许多跨国制造企业和重工业企业的首选服务器系统。
4) 硬件定制派:嵌入式构建系统
对于需要针对特定硬件开发板进行底层研发的场景,企业通常不会下载一个现成的安装盘,而是使用专门的构建工具。
- Yocto Project / Buildroot:严格来说它们不是发行版,而是“用来制造发行版的工具”。企业利用这些工具,可以精准挑选需要的内核模块和系统组件,编译出体积极小、专门适配特定硬件架构的定制化 Linux 系统。
4.什么是操作系统(OS) ?

了解了 Linux 的背景和企业应用现状后,再往下深挖,回归到“操作系统(Operating System, OS)”最基础的概念和定位,如果把计算机系统比作一家庞大且复杂的现代化工厂,那么操作系统就是这家工厂的“总调度中心”兼“大管家”。
以下是对 OS 概念和定位的详细拆解:
1) OS 的核心概念:它到底是什么?
用最严谨的话来说:
操作系统是一组控制和管理计算机软硬件资源、合理对各类作业进行调度,以及方便用户使用的系统软件集合。
为了深刻理解这个概念,我们可以从两个截然不同的视角来看待它:
1. 自上而下的视角(Top-Down):OS 是一个“魔术师”(扩展的机器)
底层的硬件实际上非常丑陋且难以使用(例如:你需要向控制器的特定寄存器写入特定的二进制指令,才能让硬盘磁头移动)。
操作系统通过提供统一的、简洁的接口(系统调用 API),把这些丑陋的硬件细节隐藏起来。在程序员和应用软件看来,OS 变出了一个干净、整洁、易于使用的“虚拟机器”。你只需要调用 write() 函数,OS 就会替你完成移动硬盘磁头、写入扇区的所有脏活累活。
2. 自下而上的视角(Bottom-Up):OS 是一个“大管家”(资源管理器)
计算机内部有 CPU、内存、硬盘、网卡等各种宝贵的资源,而同时运行的程序(如微信、浏览器、游戏)又很多,它们都在争抢这些资源。
操作系统的核心任务就是 “复用(Multiplexing)”:
- 时间复用:让多个程序轮流使用 CPU(你感觉几个程序在同时运行,其实是 OS 在毫秒级地极速切换)。
- 空间复用:把内存划分成不同的区域,安全地分配给不同的程序,确保它们互不干扰。
2) OS 的核心定位:它在计算机体系中处于什么位置?
操作系统的定位,用一个词概括就是 “承上启下”。它处在底层硬件与上层应用之间,负责管理资源并向上提供统一接口;不能简单理解为计算机中“唯一一个”同时与软硬件发生关系的实体。
我们可以通过一个经典的层次架构来理解它的定位:
- 最底层:纯硬件 (Hardware)
- 包括 CPU、内存、硬盘、网卡、显卡等物理设备。
- 核心层:操作系统内核 (OS Kernel)
- 这是 OS 的绝对核心(Linux 的本质就是这个)。它直接与硬件交互,拥有最高的系统权限(运行在内核态)。它负责管理资源、调度任务、响应硬件中断。
- 系统服务与接口层:系统调用、系统库与 Shell 等用户空间工具
- 普通程序不能随意直接操作内核内部数据和硬件,而是通过 系统调用(System Calls) 等受控接口请求内核服务。POSIX 更准确地说是一套接口/行为标准,其中很多 API 最终会调用系统调用。Shell 和 GUI 则属于用户空间程序,通过这些接口与操作系统交互,并不是“系统调用本身”。
- 最上层:应用程序 (Applications)
- 浏览器、数据库、你的 C++ 后端服务、或者一个 Python 脚本,都运行在这一层(运行在用户态)。它们完全依赖 OS 提供的接口来生存。
3) OS 的四大核心职能(管理职责)
作为大管家,操作系统的日常工作主要分为四大板块:
1. 进程与处理机管理 (CPU / Process Management)
- 概念:决定哪个程序此刻可以使用 CPU,使用多长时间。
- 任务:创建和销毁进程、进程间的通信(管道、信号量等)、处理死锁、调度算法(如时间片轮转、优先级调度)。
2. 内存管理 (Memory Management)
- 概念:为每个程序分配安全的运行空间。
- 任务:内存的分配与回收。最核心的技术是虚拟内存(Virtual Memory),它让每个程序都误以为自己拥有连续且庞大的内存空间,同时通过“分页机制”将虚拟地址映射到真实的物理内存或硬盘的交换区(Swap)上。
3. 文件系统管理 (File System Management)
- 概念:负责数据的持久化存储和组织。
- 任务:将硬盘上的二进制数据块,组织成人类可读的“目录(树形结构)”和“文件”。管理文件的读、写、执行权限(这就是 Linux 下
rwx权限的来源)。
4. 设备与 I/O 管理 (Device Management)
- 概念:管理各种极其复杂的外部设备(键盘、鼠标、打印机、网卡)。
- 任务:通过“设备驱动程序(Device Drivers)”,将不同厂商硬件的差异性屏蔽掉,向上层提供统一的操作接口(在 Linux 中,有一句名言叫“一切皆文件”,哪怕是一个网卡或一块键盘,OS 都把它们当作文件来读写)。
4) 总结
当你写下一段 C++ 代码并运行时:
- 编译器将其翻译成机器码。
- 操作系统创建一个进程,为它分配内存,将机器码加载进去。
- 当你的代码需要请求网络时,操作系统接管控制权,驱动网卡发送数据包。
- 当程序访问了无权访问或不存在的虚拟内存区域时(很多野指针、越界访问最终可能表现为这种情况),操作系统通常会向进程发送
SIGSEGV。程序若没有处理该信号,通常会终止,也就是常见的 Segmentation Fault。但并不是所有越界访问都会被操作系统立刻发现。
所以,深入理解操作系统,是真正写出高性能、高并发、安全健壮的企业级底层应用的前提。
综上所述:严格来说 Linux 首先指一个开源的操作系统内核;日常所说的 Linux 系统通常指基于 Linux 内核构建的完整操作系统。 开源有利于代码审查、协作和持续改进,但“开源”本身并不自动等于一定更高效、更稳定、更安全,最终还取决于具体设计、配置、更新和运维。
补充:什么是posix
POSIX(Portable Operating System Interface,可移植操作系统接口)是一套由 IEEE 制定的操作系统接口标准,目的是让程序在不同类 Unix 系统之间更容易移植;它规定了很多常见系统调用和库接口的行为,例如文件操作 open/read/write/close、进程 fork/exec/wait、线程 pthread、信号、目录、时间等。Linux、macOS 等都大量遵循 POSIX,所以一段按照 POSIX 接口写的 C/C++ 程序,通常可以较容易地在不同 Unix-like 系统上编译运行;POSIX 不是某个操作系统,而是一套“操作系统应该提供哪些接口、这些接口怎么工作”的标准规范。
第二章:Linux下基本指令
快捷方式出现了退出不了情况,使用
Ctrl + C,可以中断当前前台正在运行的操作。
ls 指令详解 (List Directory Contents)
语法:ls [选项] [⽬录或⽂件]:对于⽬录,该命令列出该⽬录下的所有⼦⽬录与⽂件。对于⽂件,将列出⽂件名以及其他信息。
注意: 很多 Linux 命令都采用类似的命令行形式,可以把常见结构提炼成一个便于记忆的 “Linux 指令常见公式”:命令 [选项] [参数/操作对象]。但不同命令的具体参数规则并不完全相同,不能理解成所有 Linux 命令都由 POSIX 强制采用这一种固定语法;具体仍以该命令的 --help 或 man 手册为准。
ls 的全称是 “list”(列出)。它的核心作用就是:查看当前目录或指定目录下,到底有哪些文件和子文件夹。 这就好比你推开一间房门(进入一个目录),用眼睛扫视一下房间里有哪些家具(文件)。
1. 最基础的用法
在终端中直接输入 ls 并回车:
ls
结果: 系统会简单地把当前目录下的文件和文件夹名称平铺显示出来。默认情况下,它不会显示隐藏文件,也不包含文件的详细属性(如大小、权限、修改时间等)。
2. 核心必备参数 (Options)
ls 之所以强大,在于它可以搭配不同的参数来实现极其丰富的查看需求。Linux 命令的参数通常以短横线 - 开头。
-a (全部,All)
- 作用: 显示所有文件,包括隐藏文件。
- 细节: 在 Linux 中,只要文件或文件夹的名字是以
.(英文句号)开头的,系统就会默认把它隐藏起来(比如.bashrc或.config)。使用ls -a就可以让它们无所遁形。
-l (长格式,Long format) —— 极其重要
注意:
ll通常是 Shell 中预先设置的别名(alias),很多环境会把它定义成ls -l或类似的ls组合,但它不是 Linux 统一规定的标准命令。不同发行版/用户配置可能不同,可用type ll或alias ll查看当前定义。
-
作用: 以
列表的形式,详细展示文件的各项属性。 -
输出拆解: 当你输入
ls -l后,会看到类似下面这样的一行输出:drwxr-xr-x 2 root root 4096 4月 14 18:00 test_folder这行信息包含了极大的信息量,从左到右依次是:
- 文件类型与权限 (
drwxr-xr-x):- 第1个字符代表类型:
d是目录(directory),-是普通文件,l是软链接(快捷方式)。 - 后9个字符分三组,代表权限:属主的权限(
rwx可读可写可执行)、所在组的权限(r-x可读可执行不可写)、其他人的权限(r-x)。
- 第1个字符代表类型:
- 硬链接数 (
2):指向该文件的硬链接数量。 - 所有者 (
root):这个文件属于哪个用户。 - 所属组 (
root):这个文件属于哪个用户组。 - 文件大小 (
4096):默认单位是字节 (Bytes)。 - 修改时间 (
4月 14 18:00):最后一次修改的时间。 - 文件/目录名 (
test_folder)。
- 文件类型与权限 (
-h (人性化阅读,Human-readable)
- 作用: 将文件大小转换为我们容易看懂的单位(如 K, M, G),而不是一长串的字节数。
- 注意:
-h的作用是把已经显示出来的大小转换成更易读的单位,所以最常见的是和-l搭配成ls -lh;它并不是语法上“只能和-l一起用”。
-t (时间排序,Time)
- 作用: 按照文件的最后修改时间进行降序排序(最新修改的文件排在最前面)。这在排查日志文件时非常有用。
-r (反转,Reverse)
- 作用: 将默认的排序结果反转。比如
ls -tr就会把最新修改的文件排在最后面(正好在你输入命令的上方,最容易看到)。
-R (递归,Recursive)
- 作用: 不仅列出当前目录的内容,还会把里面所有子目录、子目录的子目录里的文件全部展开列出来(就像一棵倒挂的树)。如果目录很深,慎用。
-d
- 作用: 将⽬录像⽂件⼀样显⽰,⽽不是显⽰其下的⽂件。如:ls‒d指定⽬录
3. 日常工作中的“黄金组合”
Linux 的参数是可以连写在一起的,不需要每个参数都加横线。以下是企业工程师每天都在敲的组合:
ls -la- 含义: 查看当前目录下的所有文件(含隐藏文件)的详细属性。
ls -lh- 含义: 查看详细属性,且文件大小以KB/MB/GB显示。
ls -lrt- 含义: 按照修改时间排队,最新修改的放在最下面(极其适合查看刚刚生成的日志或报错文件)。
4. 底层 OS 概念小扩展
结合我们之前讲的操作系统知识:当你敲下 ls 时,操作系统到底在干什么?
在 Linux 的文件系统中,“目录”其实也是一种特殊的文件。当你运行 ls 时,内核实际上是在读取这个“目录文件”里面的数据块,解析出里面存放的文件名列表以及它们对应的 inode(索引节点,存放文件属性的数据结构),最后格式化输出到你的屏幕上。
补充:xshell如何连接云服务器
1.会话: 先左边图标新建再右边图标连接
2.命令行: xshell终端界面输入:
ssh 用户名(root之类的)@公网ip
补充:参数合并
在 Linux 的命令行设计中,很多使用单个短横线(-)引导的单字母短选项,可以合并写在同一个 - 后面。但这是否允许合并、顺序是否影响结果,要看具体命令的参数解析规则,不能概括成“所有短参数都能随便合并,而且完全没有顺序”。
1. 确实可以合并写在一起
对于像下面这些允许组合的单字母短选项,你不需要每次都敲一个短横线。
- 分开写:
ls -l -a -h -t - 合并写:
ls -laht(这就是日常开发中最标准、最偷懒的敲法)
对这里 ls 的这些开关型选项来说,两种写法表达的是同一组选项,实际效果相同。参数是由 ls 程序解析的,不是 Linux 内核在解析。
2. 绝大多数情况下,顺序无关紧要
对于像 ls 这样,参数仅仅起到“开关”作用(比如开启隐藏文件显示、开启详细列表输出)的命令,参数排列的先后顺序完全不影响最终结果。
ls -lahtls -thalls -halt
对这里列出的 ls 开关型选项来说,这三种写法通常都能识别出同一组选项。但不能据此推出所有命令、所有选项的顺序都永远无关。
⚠️ 3. 必须要小心的一类情况(带值的参数)
这是日常操作中极其容易报错的地方:如果某个单字母选项本身还需要一个“具体的值”或“目标名称”,就必须特别注意这个命令如何解析后续字符。为了避免歧义,最稳妥的习惯是把这种选项单独写,或者在确认该命令支持组合写法时把它放在组合选项的末尾。 这是一条安全写法,不是所有命令都由语法强制要求“带值选项必须永远放最后”。
最经典的例子是文件归档压缩命令 tar:
-c:创建一个新的归档-z:调用 gzip 进行压缩-v:终端显示打包过程-f:指定归档文件的名称(这个参数的后面,必须立刻接上文件名!)
正确的写法:tar -czvf my_project.tar.gz ./test_folder
在这里,f 被放在了字母串的最后,所以系统会将紧随其后的 my_project.tar.gz 准确识别为归档文件名。
导致错误的写法:tar -cvfz my_project.tar.gz ./test_folder
如果你随便打乱顺序把 f 放到了中间,系统会认为你想把文件打包并命名为 z,随后面对 my_project.tar.gz 这个多余的字符串报出语法错误。
4. 补充说明:长参数不能像单字母短选项那样拼成一个字母串
你偶尔还会遇到以双短横线(--)开头,由完整英文单词组成的“长选项”(例如 ls --color=auto 或 ls --help)。
这种长参数通常需要完整写出选项名,不能像 -lah 那样把多个长选项压缩成一个字母串。某些长选项本身可以采用 --选项=值 的形式,但那是给一个选项传值,不是把多个长选项合并。
补充:Linux下目录/文件/文件夹是什么?
简单来说,“目录”和“文件夹”是同一个东西的两种叫法,而 “文件”则是用来装具体数据的容器。但在 Linux 的底层哲学中,它们的界限又比 Windows 中要模糊得多。
1. 文件夹 (Folder) vs 目录 (Directory)
这俩本质上是完全一样的概念。
- 文件夹 (Folder):这是我们在 图形界面(GUI) 中常用的通俗叫法。比如 Windows 或 macOS 桌面上那个黄色的、可以双击打开的图标,我们习惯叫它文件夹。
- 目录 (Directory):这是我们在 命令行(CLI) 或底层技术文档中使用的专业术语。在 Linux 终端敲击命令(如
cd、mkdir、ls)时,我们操作的都是“目录”。
总结:如果你在跟工程师交流,或者看技术文档,请尽量使用“目录”这个词,会显得更专业。如果你是在跟普通用户解释,说“文件夹”他们更容易懂。
2. 目录 (Directory) vs 文件 (File)
在用户的直观感受中,它们的区别很明显:
- 文件 (File):是存储实际数据的实体。比如你写的一段 C++ 代码(
.cpp)、一张照片(.jpg)、一段文本(.txt),它们都有具体的内容(字节数据)。文件不能再包含其他文件。 - 目录 (Directory):是一个容器。它本身不存储像代码或照片那样的“业务数据”,它的作用是组织和存放文件或其他目录。
💡 Linux 的底层哲学:“一切皆文件” (Everything is a file)
这是 Linux 与 Windows 最本质的区别之一!
在 Linux 中,目录本身也是一种特殊的文件类型。它和普通文件的数据组织方式不同,但同样具有 inode、权限、时间戳等文件系统属性;因此不能说“根本没有目录”这个概念。
- 普通文件 (Regular File):存放普通数据。
- 目录文件 (Directory File):这也是一个文件,只不过它里面存放的数据不是你写的代码,而是“文件名”和“对应的底层存储地址(inode)”的映射表。(普通文件(存放数据内容)和目录文件(存放其他文件列表的节点)是截然不同的底层结构)
- 当你执行
ls命令时,系统其实是在读取这个“目录文件”里的映射表数据。
- 当你执行
3.文件=文件的内容+文件的属性
1) “文件 = 文件的内容 + 文件的属性”
在 Linux 底层(如 ext4 文件系统),文件被严格地劈成了两半存储:
- 文件的属性(Metadata / 元数据):核心元数据记录在
inode(索引节点) 中,例如文件类型、大小、修改时间、所有者、读写权限(rwx)、数据块索引等。不同文件系统支持的时间字段并不完全一样,例如“创建时间 / birth time”并不是所有 Linux 文件系统都一定提供。inode 不直接保存普通文件里的真实业务内容。 - 文件的内容(Data):存放在物理硬盘的
Block(数据块) 中。这里只存纯粹的二进制业务数据(比如你的代码、视频画面)。
2) “ls 其实是描述文件的属性”
- 当你敲下
ls -l时,系统在底层其实是在 “查户口”。它读取的是文件的属性(也就是inode) 信息,然后把属性打印在屏幕上。ls根本不在乎文件里面写了什么,哪怕是一个几个 GB 的电影文件,ls瞬间就能显示结果,因为它没有去读取庞大的内容数据块。 - 相对的,如果你敲下
cat main.cpp(查看文件内容),系统才会根据inode的指引,去硬盘的Block里把你的代码一行行读出来显示。
3) “空文件夹指的是内容空,而不是属性空,甚至连“内容空”都不是绝对的零!”
- 属性绝不为空:一个刚刚创建的“空文件夹”,依然拥有完整的
inode。它有创建时间、有属于你的权限(比如drwxr-xr-x)。 - 内容其实也不完全为空:如果你用
ls -la去看一个新建的空目录,你会发现它里面至少有两个隐藏项:.(当前目录自身的映射)..(上一级目录的映射)
空文件(普通文件) 没有这种结构。用 touch 新建一个空文件,ls -l 会显示大小是 0 字节,ls -la 也只会显示它自身的一条记录,没有隐藏的 . 和 … 在里面。它的数据块是空的,内容就是绝对的零。
所以空文件和空目录一样,属性永远不空;但空文件的内容是绝对的零,没有像目录那样的“隐藏自身映射”。
一个有趣的冷知识:
在 Linux 中,一个“空文件夹”的大小通常显示为 4096 字节(4KB),而不是 0 字节。
为什么?因为哪怕它里面没有存放任何用户的子文件,目录本身也需要保存目录项等文件系统数据,其中逻辑上还包含 . 和 .. 这样的特殊目录项。在常见 ext 系文件系统中经常会看到空目录大小显示为 4096 字节,但具体大小由文件系统和实现决定,并不是 Linux 统一规定“空目录必须正好占 4KB”。
4. 如何在终端中快速区分它们?
既然在底层它们都是文件,我们在敲命令时该如何区分呢?
最常用也是最可靠的方法,就是我们之前学过的 ls -l(或者 ll)命令:
mounanlin@ubuntu:~$ ls -l
drwxr-xr-x 2 mounanlin root 4096 4月 14 18:00 test_project
-rw-r--r-- 1 mounanlin root 128 4月 14 18:05 main.cpp
- 看最左边的第一个字符:
- 如果是
d(directory),说明这是一个目录(如上面的test_project)。 - 如果是
-,说明这是一个普通文件(如上面的main.cpp)。 - (如果是
l,说明是快捷方式/软链接)
- 如果是
补充:linux文件类型
在 Linux 的世界里,“一切皆文件”是核心哲学。但这并不意味着所有文件都是一样的,Linux 通过不同的文件类型来区分这些“文件”到底扮演什么角色。
注意:与 Windows 上很多应用习惯依赖扩展名不同,Linux 内核通常不会单靠文件后缀决定文件类型或是否可执行。但后缀仍然有命名和应用识别意义;Linux 的
file指令会进一步分析文件内容(包括“幻数” Magic Number 等规则)来判断其真实类型。
除了我们最熟悉的“普通文件”和“目录”,Linux 还有几种特殊的类型,它们共同支撑起了系统的运行。
如何查看文件类型?
最简单的方法是使用 ls -l(或 ll)。在输出结果的最左侧,第一个字符就代表了该文件的类型。
drwxr-xr-x 2 user user 4096 4月 14 18:00 test_dir
-rw-r--r-- 1 user user 128 4月 14 18:05 main.cpp
l rwxrwxrwx 1 user user 7 4月 14 18:10 link_to_file -> main.cpp
七大核心文件类型
我们可以将 Linux 的文件类型分为以下七类,你可以根据首字母来快速识别:
1. 普通文件 (Regular File) —— 标识符:-
- 描述:这是最常见的文件。它可以是纯文本(代码、配置)、二进制可执行程序、压缩包、图片或视频。
- 特点:它们在物理磁盘上占据数据块(Blocks),存储实际的业务信息。
2. 目录文件 (Directory File) —— 标识符:d
- 描述:即“文件夹”。
- 底层真相:如我们之前所聊,目录在 Linux 中也是一种特殊文件,其内容是一张“文件名 + Inode 号”的映射表。
3. 符号链接 (Symbolic Link) —— 标识符:l
- 描述:类似于 Windows 的“快捷方式”。
- 底层真相:这类文件的内容其实是另一个文件的绝对路径或相对路径字符串。当你访问它时,内核会自动将请求重定向到指向的目标。
4. 字符设备文件 (Character Device) —— 标识符:c
- 描述:代表能够像字符流一样被访问的设备。
- 示例:键盘、鼠标、串口。
- 特点:数据是按顺序一位一位处理的,通常不支持随机访问。你可以在
/dev目录下看到大量此类文件(如/dev/tty)。
5. 块设备文件 (Block Device) —— 标识符:b
- 描述:代表能够进行随机访问的硬件设备。
- 示例:硬盘、U 盘、光驱。
- 特点:数据是以“块”(通常是 512 或 4096 字节)为单位进行传输和缓存的。你在
/dev下看到的/dev/sda就是你的硬盘。
6. 管道文件 (FIFO / Pipe) —— 标识符:p
- 描述:用于进程间通信(IPC)。
- 特点:它就像一根水管,一个进程从一头往里写数据,另一个进程从另一头读。它是先进先出的(First In First Out)。
7. 套接字文件 (Socket) —— 标识符:s
- 描述:用于跨网络或本地进程间的双向通信。
- 示例:数据库(如 MySQL)通常会生成一个
.sock文件,让本地的应用程序通过它快速连接数据库,而不需要走繁琐的网络协议栈。
一个进阶小知识:file [选项] 文件名 指令
Linux 终端 / Shell 中,注释符号是 #命令正常运行
`
有时候你会发现,即便知道了是“普通文件(-)”,你还是不知道它是文本还是二进制。这时可以使用 file 命令:
file main.cpp # 输出: C++ source, ASCII text
file /bin/ls # 输出: ELF 64-bit LSB shared object (可执行程序)
1. -b (Brief) —— 只要结果,不要废话
通常 file 会输出 文件名: 类型。如果你在写脚本,只需要类型,文件名就成了干扰。
- 默认输出:
test.c: C source, ASCII text - 加上
-b:C source, ASCII text
直观理解:就像问“这件衣服是什么料子?”,
-b让专家省去“这件衣服是…”的前缀,直接说“纯棉”。
2. -i (Mime) —— 互联网标准的“身份证”
普通的描述(如 UTF-8 Unicode text)是给人看的,而 -i 输出的 MIME 格式(如 text/plain)是给程序(比如浏览器、邮件服务器)看的。
- 输出示例:
text/x-c++; charset=utf-8
直观理解:普通模式是“中文名”,
-i模式是“国际护照”。
3. -L (Link) —— 别看“路牌”,看“终点”
Linux 里的“快捷方式”叫符号链接。默认情况下,file 只会告诉你“这是一个链接”。
- 默认:
file my_link-> 结果是symbolic link to /usr/bin/python3 - 加上
-L:file -L my_link-> 结果是ELF 64-bit LSB executable...(直接告诉你链接指向的那个程序的类型)
直观理解:路牌上写着“去北京”,
-L是让你直接瞬移到北京看看那儿到底长啥样。
4. -f (File) —— 照着“清单”查
如果你有 100 个文件要查,一个一个敲文件名太累。你可以把这 100 个文件名写进一个 list.txt 里。
- 操作:
file -f list.txt
直观理解:就像点名册,专家对照名字一个一个辨别。
5. -z (Zip) —— 隔着“箱子”看货
通常 file 看到 .gz 或 .zip 文件只会说是“压缩包”。加上 -z 后,它会尝试钻进压缩包看里面装的是什么。
- 操作:
file -z archive.tar.gz
直观理解:像 X 光机,不拆快递箱也能看到里面装的是书还是水果。
核心原理:为什么它这么准?file 指令主要依据 libmagic 的 magic 规则数据库来识别内容。不同发行版的数据库位置可能不同,也可以加载本地规则;因此不能固定理解成只读取 /etc/magic 这一个文件。
补充:Linux路径的认识
在常见的 Linux 交互式登录环境中,用户登录后通常会从自己的 “家目录” (Home Directory) 开始;新打开的终端默认工作目录也经常是家目录,但终端软件和 Shell 可以被配置成从其他目录启动。桌面本质上也对应一个目录。
在 Linux 的世界里,如果说 ls 是你的眼睛,cd 是你的双腿,那么 “路径(Path)”就是你手中的地图。
Linux 的文件系统架构与 Windows 有着本质的区别。理解路径,是告别新手村、真正在终端里健步如飞的关键。
一、 一棵倒挂的树:Linux 的目录结构
在 Windows 中,你有 C盘、D盘、E盘,每个盘符都是一棵独立的树。
但在 Linux 中,世界是由一棵“倒挂的巨树”构成的,没有盘符的概念。如下图:
- Linux 文件系统可以近似理解成一棵多叉目录树。只有目录才能继续包含目录项,因此能继续向下展开的节点是目录;普通文件、设备文件、套接字、软链接等通常表现为叶节点,空目录也可以成为叶节点。
- 根目录
/(Root):这是整棵树的起点,是所有文件和文件夹的终极老祖宗。系统里的任何文件,不管它在哪个硬盘上,最终都能追溯到这个/目录下。 - 所有的文件和目录,都像树枝和树叶一样,从根目录
/向下蔓延。比如/home、/etc、/var都是直接挂在根目录下的“粗树枝”。
注意: 在 Linux 这棵庞大的目录树中,为了防止迷路,系统在创建一个新的目录时,会强制在这个目录的“映射表”里写死两条自带的隐藏记录:.(当前目录):它的底层(inode)指向这个目录自己。这样当你在终端执行 ./可执行文件 时,系统才知道是从当前坐标起步。..(父目录):它的底层(inode)指向这个目录的上一级目录。正因为映射表里有了这条记录,你敲下 cd … 时,操作系统才知道该把你往哪个具体的底层地址传送。
补充: Linux 的各目录作用


Linux 的目录结构初看可能像是一团乱码(全是 bin、var、etc 这样的神秘缩写),但实际上它遵循着一套极其严谨的国际标准——FHS (Filesystem Hierarchy Standard,文件系统层次结构标准)。
为了让你更容易记住,我把这些目录按功能属性分成了几大门派:
1. 核心指挥部(系统命令与库)
这些目录构成了 Linux 能跑起来的最基本骨架。
/bin(Binaries):传统上存放所有用户都可以使用的基础命令程序(二进制可执行文件),比如ls、cp、cat等。cd是 Shell 内置命令,并不是/bin/cd这种普通外部程序。 现代一些发行版采用 usr-merge,此时/bin还可能链接到/usr/bin。/sbin(System Binaries):存放 超级管理员(root) 才能使用的系统级管理命令。比如fdisk(磁盘分区)、ifconfig(配置网络)、reboot(重启)。普通的bin是给老百姓用的,sbin是给皇上用的。/lib和/lib64(Libraries):动态库的家。存放着/bin和/sbin中程序运行所依赖的基础共享库(如libc.so),以及内核模块。/lib64专门放 64 位的库。
2. 神经中枢(配置与启动)
/etc(Etcetera):系统配置文件的大本营,几乎所有系统级软件的配置文件都在这*。比如网卡配置、密码文件(/etc/passwd)、软件的默认设置。修改这里的任何文件,都会影响整个系统的运行行为。/boot:存放系统启动时需要用到的核心文件。包括 Linux 的内核文件(vmlinuz)和引导加载程序(如 GRUB)。如果不小心把这个目录删了,系统下次重启就直接起不来了。
3. 私人领地(用户数据)
/home:普通用户的家目录常见存放位置。如果你新建了一个叫whb的用户,常见家目录就是/home/whb。你下载的文件、写的代码、个人的配置文件(如.bashrc)通常都在这里。不同用户之间能否读取彼此文件,最终由目录/文件权限、ACL 等规则决定。/root:超级管理员(root)的专属家目录。 它不在/home下面,而是直接放在根目录下,彰显其特殊地位。
4. 软件安置区(应用程序)
/usr(Unix System Resources):最庞大的目录,类似于 Windows 的C:\Program Files。很多你用apt或yum安装的软件都会装在这里。- 里面还有子目录:
/usr/bin(普通软件的可执行文件)、/usr/lib(软件库)、/usr/local(通常是你自己手动编译安装软件的默认位置)。
- 里面还有子目录:
/opt(Optional):存放一些第三方的大型商用软件或者非系统基础环境的独立软件包(比如某些数据库、特定版本的 JDK 等)。
5. 动态数据流(随时变化的文件)
/var(Variable):顾名思义,专门放大小会不断变化的文件。比如系统的日志文件(/var/log)、数据库的数据文件、缓存文件等。/tmp(Temporary):临时垃圾桶。存放程序运行时的临时文件。系统重启时,或者文件存放超过一定时间后,这里面的东西通常会被自动清空。
6. 外设连接站(硬件与挂载)
/dev(Devices):Linux “一切皆文件” 的终极体现。 你的硬盘、鼠标、键盘、打印机,在这里都表现为一个单独的文件(比如/dev/sda代表第一块硬盘)。/mnt(Mount):系统管理员用来临时挂载外部文件系统的地方(比如你想临时挂载一块新硬盘)。/media:系统自动挂载可移动介质的地方(比如你插进一个 U 盘,系统通常会自动把它挂载到/media/usb下)。
7. 虚拟倒影(内核与进程状态)
这俩目录里的东西并不存在于硬盘上,而是存在于内存中,是内核运行状态的实时投影!
/proc(Processes):虚拟文件系统。里面存放着当前系统正在运行的所有进程的信息。每个进程都会在这里有一个以数字(PID)命名的文件夹。/sys(System):类似于/proc,但它主要展示的是硬件设备和内核模块的信息架构,是现代 Linux 内核管理硬件的接口。
二、 绝对路径 vs 相对路径
通过路径标识文件唯一性,描述一个文件在系统里的位置,我们有两种表达方式:
1. 绝对路径 (Absolute Path) —— 给出完整的GPS坐标
- 定义:永远以根目录
/开头。它描述的是目标文件在整棵树里的绝对物理位置。 - 特点:无论你现在身处哪个目录,使用绝对路径都能精准无误地找到目标。它非常安全,不会因为你当前位置的改变而失效。
- 示例:
/home/mounanlin/blog/index.html(从根出发,先找 home,再找 mounanlin,再找 blog…)/etc/nginx/nginx.conf
# 当前在任意目录,都能用绝对路径访问
cat /etc/passwd # 查看密码文件
ls /home/username/Documents # 列出用户文档目录
cd /usr/local/bin # 切换到该目录
/usr/bin/python3 script.py # 用绝对路径执行 Python
2. 相对路径 (Relative Path) —— 以你当前位置为起点的指路牌,随时随着你的当前位置而变化。
- 定义:绝对不能以
/开头。它描述的是目标文件相对于你当前所在位置的路线。 - 特点:敲起来省事、简短,但极度依赖你当前的所处位置(
pwd的结果)。如果你的位置变了,同样的相对路径可能就找不到原来的文件了。 - 示例:假设你现在正站在
/home/mounanlin目录下。- 你想进入里面的
blog文件夹。- 相对路径写法:
cd blog(直接写名字,系统默认从当前目录开始找)。 - (相比之下,绝对路径写法是
cd /home/mounanlin/blog,这就显得很啰嗦了)。
- 相对路径写法:
- 你想进入里面的
1.绝对路径可以在给定文件系统命名空间中从根目录定位目标;相对路径则以当前工作目录为基准,当前位置改变后,同一个相对路径的含义可能变化。
2.绝对路径适合需要“无论从哪里执行都定位固定位置”的场景;相对路径更简洁,适合围绕当前项目操作。不存在“配置文件必须用绝对路径、其他常规操作统一用相对路径”的统一规则。
# 当前在 /home/zhang
ls Documents # 等同于 /home/zhang/Documents
cd Downloads # 进入 /home/zhang/Downloads
cat ../.bashrc # 上一级目录 (/home) 下的 .bashrc
cd ../zhang/Desktop # 先回到 /home,再进 zhang/Desktop
ls ./Pictures # 显式指明当前目录下的 Pictures
三、 必须要记住的 4 个“魔法符号”
为了配合相对路径,Linux 提供了几个极其重要的特殊符号:
.(一个英文句号):代表当前目录。- 应用场景:当你想运行当前目录下的一个自己写的脚本
test.sh时,直接输入test.sh系统是找不到的(出于安全机制),你必须明确告诉系统“就在当前目录下找”,写成./test.sh。
- 应用场景:当你想运行当前目录下的一个自己写的脚本
..(两个英文句号):代表上一级目录(父目录)。- 应用场景:假设你在
/home/mounanlin/blog,敲cd ..就会退回到/home/mounanlin。
- 应用场景:假设你在
~(波浪号):代表当前登录用户的家目录。- 应用场景:无论你在哪里,
cd ~都能直接进入当前用户的家目录。普通用户的家目录通常位于/home/用户名,但 root 通常是/root,而且管理员也可以把家目录配置到其他位置,所以~不应机械理解为固定的/home/用户名。
- 应用场景:无论你在哪里,
-(短横线):代表你上一次所在的目录。- 应用场景:在两个极深的绝对路径之间来回横跳时,
cd -是神器。
- 应用场景:在两个极深的绝对路径之间来回横跳时,
补充:根目录和家目录
既然我们刚才聊到了 Linux 的世界观是一棵完整的“单根树”,那么理解这棵树上的两个最特殊的地标——根目录和家目录,就是掌握 Linux 空间感的最后一步。
很多人初学 Linux 时会把这两个概念搞混,因为在中文语境里,“根”和“家”听起来都像是“大本营”。但其实它们在系统架构和权限管理上有着天壤之别。
我们可以用 “一栋单身公寓大楼” 来做类比:
1. 根目录 (Root Directory):/
—— 整栋公寓大楼的地基与公共区域
- 符号代表:一个简单的正斜杠
/。 - 绝对起点:它是整个 Linux 文件系统的最顶端。在这棵大树上,所有的文件、所有的目录(包括所有的外接硬盘和 U 盘),最终都挂载在这个
/之下。在这个符号上面,什么都没有了。 - 大楼类比:根目录就像是大楼的入口大堂、设备间、电梯井和走廊。
- 里面有什么:
/bin:存放所有用户都能用的基础命令(比如ls,cp)。/etc:存放整个系统的全局配置文件(大楼的物业管理规则)。/dev:存放所有的硬件设备文件。
- 权限极严:作为一个普通住户,你可以去大堂或走廊“看一看”(拥有读取权限),但你绝对不能在大堂里随便放自己的私人物品,或者去改设备间的电线(没有写入权限)。只有大楼的超级管理员(也就是
root用户)才有资格在根目录下随心所欲地操作。
2. 家目录 (Home Directory):~
—— 属于你自己的那间私房
- 符号代表:波浪号
~ - 相对位置:家目录并不是独立于根目录之外的,它其实是根目录这棵大树上的一个分支。通常情况下,所有的普通用户的家目录,都会被统一放在
/home这个目录下。 - 大楼类比:这就是你用钥匙打开的那间属于你的公寓房间。
- 里面有什么:
- 你的私人文件、下载的代码、项目工程。
- 只有对你生效的个性化配置文件(通常是隐藏文件,比如配终端主题的
.bashrc,存 SSH 密钥的.ssh/)。
- 绝对自由(通俗理解):在自己的家目录里,用户通常拥有较高的读写权限,可以自由创建项目和个人配置。但实际权限仍由文件权限、ACL、只读挂载、磁盘配额等规则决定,并不是系统一定不会阻拦。
3. 一图看懂它们的物理关系
如果我们在终端里用之前学过的 tree 宏观地看一看,它们的结构是这样的:
/ (根目录 - 整个系统的绝对顶点)
├── bin/
├── etc/
├── var/
└── home/ (普通用户的宿舍楼)
├── user1/ <-- 这是 user1 的家目录 (~)
├── user2/ <-- 这是 user2 的家目录 (~)
└── developer/ <-- 假设你的登录名是 developer,这就是你的家目录 (~)
├── Documents/
└── code_projects/
从这里可以清晰地看出:对于普通用户的常见配置,家目录通常是 /home/你的用户名/;更准确地说,~ 是 Shell 对“当前用户家目录”的快捷展开,root 的 ~ 通常是 /root,家目录也可以配置到其他位置。
4. 终端里极易用错的穿梭指令
理解了它们的区别,你在使用 cd (Change Directory) 穿梭时就不会迷路了:
cd /:瞬间传送到整棵大树的根部。此时你如果敲ls,看到的一定是bin,etc,home这些系统级文件夹。cd ~:瞬间传送到你的私房。无论你当前迷失在系统的哪个深枝末叶里,敲下它就能一键回城。cd:(后面什么都不加)这和cd ~完全等价,是开发者最爱的一键回城快捷键。
tree 指令
如果说 ls 是拿着手电筒在当前房间里照来照去,那么 tree 就是直接开启了 “上帝视角”。
它的核心作用是:以树状图的格式,直观地列出目录及其所有子目录和文件的层级结构。
对于需要频繁整理项目结构、写项目文档的开发者来说,tree 是生成目录结构图的神器。
(注:在一些极简的 Linux 系统中,tree 不是自带的,首次使用可能需要通过 sudo apt install tree 或 yum install tree 安装。)
1. 基本语法
tree [选项] [目录路径]
2. 核心用法与实战场景
场景一:直接查看当前层级(最基础)
如果不加任何参数,直接敲 tree,它会从当前目录开始,向下遍历所有的子文件夹和文件,并画出一棵漂亮的树:
tree
输出示例:
.
├── CMakeLists.txt
├── include
│ ├── config.h
│ └── math_utils.h
└── src
├── main.cpp
└── math_utils.cpp
2 directories, 5 files
场景二:⭐ 限制遍历深度(最核心参数 -L)
这是 tree 最重要、最保命的参数。如果你的项目非常庞大(比如包含了整个 node_modules 或者庞大的构建输出目录),直接敲 tree 会直接满屏滚动半小时。
使用 -L (Level) 可以精确限制它向下钻取的层数。
只看当前目录下的第一层和第二层结构
tree -L 2
查看 /etc 目录,但只看向下 1 层的结构(相当于带有树状格式的 ls)
tree -L 1 /etc
场景三:只看骨架,不看血肉(只显示目录 -d)
在接手一个新项目或者向别人展示代码架构时,我们往往只关心文件夹是怎么划分的,不想看里面密密麻麻的具体代码文件。使用 -d (directories) 可以只显示文件夹。
# 只显示文件夹结构,忽略所有文本和代码文件
tree -d
场景四:查看隐藏文件(-a)
和 ls 的逻辑一样,tree 默认会忽略以 . 开头的隐藏文件(比如 .git 或 .vscode 文件夹)。加上 -a (all) 就能让它们现形。
tree -a
3. 极客组合技(写文档神器)
当你写 CSDN 博客或者做项目总结时,经常需要把项目结构粘贴到 Markdown 文档里。你可以把 tree 的输出结果直接重定向到一个文本文件中,拿来就用:
# 生成最多包含 2 层的目录树,并直接保存到 structure.txt 文件中
tree -L 2 > structure.txt
🚨 开发者避坑指南
绝对不要在根目录(/)或用户家目录(~)下直接敲没有 -L 参数的 tree!
系统会试图遍历你整个硬盘里的几百万个文件并画树,你的终端会陷入疯狂的字符狂欢中。如果不小心按错了,记住最快速度按下 Ctrl + C 强行中断它!
pwd 指令详解
在 Linux 的命令行世界里,如果没有图形界面的“地址栏”作为指引,你很容易在庞大的文件树(多叉树)中迷失方向。
pwd 就是你的“全宇宙通用 GPS 定位仪”。
它的全称是 Print Working Directory(打印当前工作目录)。虽然这个命令看起来极其简单,连参数都很少用到,但它背后却隐藏着与“软链接(l)”息息相关的底层逻辑。
下面为你详细拆解 pwd:
1. 核心作用:灵魂拷问“我现在到底在哪?”
在终端中,你只需要输入这三个字母并回车:
mnl@ubuntu:~/projects$ pwd
输出结果:/home/mounanlin/projects
- 绝对路径输出:
pwd永远、绝对地返回你当前所在位置的绝对路径(以根目录/开头的完整路径)。它绝不会给你一个模棱两可的相对路径。 - “工作目录”的含义:Working Directory 就是你当前正在“站立”的节点。你接下来的所有相对路径操作(比如
ls ./或rm *.txt),系统都会默认以这个目录为基准点进行计算。
2. 底层揭秘:系统是怎么知道你在哪的?
系统真正知道“你当前在哪”靠的是 CWD(Current Working Directory,当前工作目录),它是操作系统为每个进程维护的一项进程状态;Shell 本身也是一个进程,所以也有自己的 CWD。与此同时,Shell 还会维护一个 PWD 环境变量,用来保存当前目录的逻辑路径字符串。执行 cd 时,Shell 会修改自己的 CWD,并同步更新 PWD;因此可以简单记成:CWD 是系统真正记录的当前位置,PWD 是 Shell 维护的路径表示。
cd指令
cd是 Change Directory(切换目录)的缩写。它的主要作用非常单纯:让你在系统内的不同文件夹(目录)之间穿梭。
1. 基本语法
cd [目标路径]
(注意:cd 和后面的路径之间必须至少保留一个空格。)
2. 核心概念:绝对路径 vs 相对路径
要真正熟练使用 cd,必须理解这两种路径的区别:
- 绝对路径:从系统的“根”开始写起的完整地址。无论你当前位于系统中的哪个角落,输入绝对路径总能把你带到那个固定的地方。
- Linux / macOS 示例:
cd /usr/local/bin(以/开头代表从根目录出发) - Windows 示例:
cd C:\Users\Admin\Documents(以盘符开头)
- Linux / macOS 示例:
- 相对路径:以你当前所在的目录为起点的地址。
- 示例:如果你当前位于
/home/user,输入cd Documents(前面没有/),你就会进入/home/user/Documents。
- 示例:如果你当前位于
3. 必会的快捷符号
这些特殊的符号是命令行的精髓,能大幅提高你的操作效率:
cd ..(返回上一级目录)..代表父目录(上一层文件夹)。如果你在Documents/work,输入cd ..就会退回到Documents。- 进阶用法:你可以串联使用,例如
cd ../..会向上连退两级。
cd ~或直接输入cd(回到家目录)~代表当前用户的“家目录”(Home Directory)。无论你迷失在文件系统的哪个深处,输入cd ~就能立刻回到你的个人主文件夹(例如 macOS 的/Users/你的用户名)。
cd -(返回上一次所在的目录,cd -只会在两个目录之间来回切换)- 这是一个超级实用的“折返”功能,类似于电视遥控器上的“返回上一个频道”。如果你从
/var/log切换到了/etc,只需输入cd -就能瞬间跳回/var/log。
- 这是一个超级实用的“折返”功能,类似于电视遥控器上的“返回上一个频道”。如果你从
cd /(前往根目录)/代表整个文件系统的最顶层。
cd .(当前目录).代表你当前所在的目录。单独执行cd .没有任何效果,但在其他操作中(例如复制文件到当前目录,或运行当前目录的脚本./script.sh)非常常见。
clear 指令
clear 是 Linux 终端中最让人“心旷神怡”的指令。它的作用非常单纯:清屏。
当你的终端屏幕被各种复杂的代码输出、报错日志或长篇手册填满,让你感到眼花缭乱时,clear 能瞬间还你一个干干净净的初始界面。
1. 基本语法
直接输入即可,不需要任何参数:
clear
2. 开发者必备的终极平替:Ctrl + L
在实际开发和日常运维中,很多老手会更习惯直接使用 Ctrl + L,而不是每次都敲 clear 这五个字母。
直接在键盘上按下组合键 Ctrl + L,在 Bash 等常见交互环境中的视觉效果与输入 clear 很接近,但两者实现机制并不完全相同。这个快捷键的优势在于:
- 速度极快。
- 不会打断当前输入:即使你当前命令行里已经敲了一半的代码(比如
cp -r src/ bac),按下Ctrl + L也会瞬间清屏,并且把你刚才敲了一半的内容完好无损地保留在屏幕最上方,你可以继续往下打字。
3. 底层真相:它真的“清空”了吗?
并没有。
clear 指令通常会根据终端能力发送相应的终端控制序列来清理/重绘可见屏幕;Ctrl + L 在 Bash/Readline 中也会触发重绘。它们并不只是简单地“输出大量空行把内容向上推”。
很多终端中,清屏后向上滚动仍可能看到之前的输出,但是否保留滚动缓冲区取决于终端模拟器、clear 的实现和相关选项;因此不能保证任何终端都一定能找回之前的全部输出。
(注:它只是清理视觉画面。如果你想清除终端记住的“你敲过哪些命令”的历史记录,防止别人按 ↑ 键偷看,那需要用到 history -c 指令。)
history 指令
history 就是你的 “命令行记忆库”。Shell 通常会把你执行过的命令按顺序记录下来,方便重复使用和检索。是否记录某条命令、保存多少条、是否去重,会受到 HISTCONTROL、HISTIGNORE、历史长度等配置影响,所以并不是所有指令都一定原封不动永久保存。
当你想不起昨天敲的那个巨长无比的编译命令,或者想重复执行某次复杂操作时,history 就是你的救命稻草。
1. 基础用法与精准查找
最基础的用法就是直接输入:
history
系统会打印出成百上千条带有编号的历史命令列表。
💡 开发者高频用法:结合 grep 过滤查找
直接看几百条记录太瞎眼了,通常我们会配合管道符 | 和 grep(文本搜索指令)来找特定的命令:
只看包含 "gcc" 这个关键词的历史命令
history | grep gcc
只看包含 "ssh" 的历史命令
history | grep ssh
2. 叹号魔法:快速调用历史命令
在 Linux 的 Bash/Zsh 中,感叹号 ! 是专门用来调用历史记录的快捷符号。掌握它们能让你敲键盘的速度起飞:
不需要先执行 history 命令。叹号魔法是 Bash/Zsh 内置的历史扩展功能,直接在终端输入就能生效。
!编号:直接执行历史列表中对应编号的命令。- 示例:你通过
history看到第 1024 条命令是g++ main.cpp -o app -std=c++20,直接敲!1024回车,它就会再次执行这条长命令。
- 示例:你通过
!字符串:执行最近一次 以该字符串开头的命令。- 示例:敲
!cp,系统会立刻执行你上一次用过的cp命令。
- 示例:敲
!!(双叹号):原样重复执行上一条命令。- 🔥 终极实战场景:你敲了一长串命令准备修改系统文件,回车后系统无情提示
Permission denied(权限不足)。此时你不需要按上箭头重新找并把光标移到最前面,直接敲sudo !!,系统就会自动把上一条命令拼在sudo后面再执行一次。
- 🔥 终极实战场景:你敲了一长串命令准备修改系统文件,回车后系统无情提示
3. 开发者必备的终极平替:Ctrl + R
就像老手不用 clear 而是用 Ctrl + L 一样,老手找历史命令极少用 history,而是用 Ctrl + R (Reverse Search)。
- 在终端直接按下
Ctrl + R。 - 终端提示符会变成
(reverse-i-search)\:。 - 开始输入你印象中的命令片段(比如输入
docker)。 - 系统会实时向回搜索并显示最近一条匹配的命令。
- 如果不是你要找的那条,继续按
Ctrl + R,它会继续往更早的历史中查找。 - 找到了!直接按下
Enter键执行,或者按右方向键把命令拉到当前输入框进行修改。
4. 底层机制与“毁尸灭迹”
history 既会使用当前 Shell 会话中的内存历史列表,也可以通过家目录下的历史文件实现跨会话保存。
- 如果你用的是 Bash,它存在
~/.bash_history里。 - 如果你用的是 Zsh,它存在
~/.zsh_history里。
(注:当前会话的新命令通常会先进入内存历史列表;何时追加/写回历史文件取决于 Shell 及其配置。多个终端同时打开时,也可能各自维护自己的历史状态。)
如何清空历史?
如果你在这个服务器上敲了一些包含敏感密码的命令,或者由于强迫症想清理记录,可以使用:
清空当前会话在内存中的历史记录(此时敲上箭头已经找不到刚才的命令了)
history -c
将内存中的“空”状态强制写入硬盘,彻底抹除历史文件里的记录
history -w
whoami 指令
1. 核心作用
whoami 的作用非常直接:打印当前终端会话的有效用户名。
它会告诉你“我现在是以什么身份在操作系统中执行操作”。
2. 基本语法
直接在终端中输入即可,不需要任何参数:
whoami
输出示例:
如果你以普通用户身份登录,可能会输出你的用户名(比如 jiaoyuan)。
如果你切换到了超级管理员,它会输出 root。
3. 常见使用场景
- 迷失环境时确认身份:当你通过 SSH 连接了多台服务器,或者接手了别人刚用过还没关闭的终端界面时,快速确认当前登录的账号。
- 确认权限切换结果:当你使用了
su(切换用户)或者在某些sudo环境下,不确定自己是否已经成功获取了root权限时,敲一下whoami就能验证。 - Shell 脚本编写:在编写自动化脚本时,经常需要限制必须由管理员运行。
whoami常被用来做权限拦截:if [ "$(whoami)" != "root" ]; then echo "权限不足,请使用 root 权限运行此脚本" exit 1 fi
4. 底层逻辑
whoami 实际上是在查询当前进程的有效用户 ID(Effective User ID),并将其解析为对应的文本名称。
它的功能与另一个指令完全等价:
id -un
(id 指令用于查看用户的 UID 和 GID 等信息,-u 表示只看用户,-n 表示显示名称而不是数字代号)。由于 whoami 更符合人类自然语言直觉,所以使用频率更高。
touch 指令
在 Linux 系统中,touch 是一个非常基础且常用的指令。虽然它的名字叫“触摸”,但它在实际工作中有两个截然不同、却都非常核心的作用。
1. 核心作用
touch 指令主要用于以下两个目的:
- 创建新的空文件(最常用的场景)。如果指定的文件不存在,
touch会立刻创建它,文件大小为 0 字节。 - 修改文件的时间属性(它最初的设计初衷)。如果指定的文件已经存在,
touch不会改变文件的内容,而是会把该文件的“最后访问时间”和“最后修改时间”更新为系统的当前时间(就像你刚刚“碰”了它一下)。
注意:
1.touch不能用来“创建目录(文件夹)”;当目标不存在时,它创建的是普通文件。但如果目标本来就是一个目录,touch仍可以用于更新这个目录的时间戳,所以不能把它理解成“touch 完全不能作用于目录”。
2.如果你只给名字(如 touch a.txt):指令会默认在“当前正踩着的土地”上操作。如果你给路径(如touch /home/user/a.txt):指令会像“远程导弹”一样,跨过当前的界限,精准打击你指定的位置。
2. 基本语法
touch [选项] 文件名
3. 常见使用场景与示例
场景一:快速创建空文件
这是日常开发中最频繁的用法。当你需要一个新文件来写代码或记录日志,但暂时不想往里面写内容时:
创建一个名为 main.cpp 的空文件
touch main.cpp
同时创建多个文件(用空格隔开)
touch index.html style.css app.js
结合大括号进行批量创建(例如创建 test1.txt 到 test5.txt)
touch test{1..5}.txt
场景二:更新已存在文件的时间戳
在某些自动化构建工具(比如 C++ 开发中常用的 make)中,系统是通过比对源文件和目标文件的时间戳来决定是否需要重新编译的。如果你修改了配置,想强制触发重新编译,就可以用 touch 更新一下源文件的时间。
# 假设 config.h 已经存在,执行此命令后,它的修改时间会变成当前时间
touch config.h
4. 进阶选项参数
如果你不想使用系统的当前时间,或者只想修改特定的时间属性,可以使用以下参数:
-a:只更改文件的访问时间(Access time)。-m:只更改文件的修改时间(Modification time)。-t:使用指定的具体时间,而不是系统当前时间。时间格式通常为[[CC]YY]MMDDhhmm[.ss]。将文件的修改时间和访问时间设置为 2026年4月15日 09点00分 touch -t 202604150900 target_file.txt-c:不创建任何文件。如果文件存在,则更新时间;如果文件不存在,什么也不做(不会报错,也不会创建新文件)。
总结
对于常规的 C++ 开发或服务器运维来说,记住 “touch 文件名 = 创建空文件” 就足以应付 90% 以上的使用场景了。
补充:touch创建位置
touch(以及所有 Linux 指令)的操作范围,从来都不局限于“当前目录”。
1. 核心逻辑:只要路径对,哪里都能建
只要你对目标位置拥有写入权限,你可以在任何地方创建文件。
场景 A:使用“绝对路径”(从根目录 / 开始算)
不管你现在人在哪里(哪怕你在家目录 ~),你都可以直接在 /tmp 文件夹下建个文件:
touch /tmp/new_file.txt
场景 B:使用“相对路径”(从当前位置开始算)
假设你现在在 Projects 文件夹里,你想在它的子文件夹 src 里面建个文件:
# 不需要先 cd src
touch src/main.cpp
2. 两个致命的“不能创建”的情况
虽然理论上哪里都能建,但实际操作中你会遇到两次“碰壁”:
第一种:路径不存在(父目录缺失)
如果你想在 a 文件夹下的 b 文件夹里建个文件,但 a 或 b 根本不存在:
touch a/b/test.txt
# 报错:touch: cannot touch 'a/b/test.txt': No such file or directory
解决办法:你得先用 mkdir -p a/b 把目录树种出来,才能在树枝上挂文件。
第二种:权限不足(越界操作)
就像我之前比喻的,你在自己的公寓(家目录 ~)可以随便折腾,但你不能去大楼走廊(根目录 / 或 /etc)乱钉钉子。
touch /etc/hacker.conf
# 报错:touch: cannot touch '/etc/hacker.conf': Permission denied
解决办法:除非你使用 sudo(超级管理员权限),但极其不建议在不熟悉系统配置时乱碰家目录以外的地方。
💡 极客总结:Linux 指令的“瞄准”法则
在 Linux 命令行里,几乎所有指令的背后都隐藏着一套通用的 “路径识别逻辑”:
- 如果你只给名字(如
touch a.txt):
指令会默认在“当前正踩着的土地”上操作。 - 如果你给路径(如
touch /home/user/a.txt):
指令会像“远程导弹”一样,跨过当前的界限,精准打击你指定的位置。
补充一个 touch 的“隐藏技能”
在 C++ 开发中,这很有用:比如你的编译器因为时间戳对不上而拒绝重新编译某个文件,你只需要 touch 一下那个源文件,编译器就会以为它刚刚被修改过,从而强制重新编译。
rm指令
rm 是 Remove(移除/删除)的缩写。这是 Linux 中最常用、也是最危险的指令之一。
它的核心作用非常纯粹:删除文件或目录。
🚨 核心警告:
在 Linux 的哲学里,默认是没有“回收站”概念的。一旦使用 rm 成功删除了文件,它就永远消失了(极难恢复)。所以敲击回车前,一定要看清自己当前所在的目录和目标路径。
1. 基本语法
rm [选项] 文件或目录
•-f 即使⽂件属性为只读(即写保护),亦直接删除
•-i 删除前逐⼀询问确认
•-r 删除⽬录及其下所有⽂件
2. 核心用法与场景
场景一:删除普通文件(最基础用法)
如果不加任何参数,rm 只能用来删除文件,不能删除目录(文件夹)。
删除当前目录下的 main.o 文件
rm main.o
同时删除多个文件
rm index.html style.css
结合通配符批量删除(例如删除所有以 .log 结尾的日志文件)
rm *.log
场景二:删除目录(必须加上 -r)
如果你想删掉一个用 mkdir 建出来的目录,直接 rm 目录名 会报错。
你必须加上 -r (recursive - 递归) 参数。它的意思是:深入到这个目录的底层,把里面的文件和子文件夹一层一层全删掉,最后把这个目录本身也删掉。
# 删除名为 build 的文件夹及其内部所有内容
rm -r build
场景三:无情抹除,拒绝确认(加上 -f)
有些时候,系统遇到只读文件或者某些特殊权限文件时,会啰嗦地问你:“你确定要删除这个受保护的文件吗 (y/n)?”
如果你的项目很大(比如清理 C++ 项目生成的庞大 out 目录),一直按 y 会崩溃。此时就需要 -f (force - 强制) 参数。
# 强制删除文件,不管文件存不存在,也不弹出任何确认提示
rm -f old_config.json
3. 高频且极其危险的组合:rm -rf
作为开发者,-rf(即 -r 和 -f 的结合)是你每天都会用到的终极清理武器:强制且递归地删除整个目录,绝不废话。
# 瞬间让 target_folder 及其里面的所有心血灰飞烟灭
rm -rf target_folder
⚠️ 开发者避坑指南:
- 绝对不要尝试针对根目录做递归强制删除。 现代 GNU
rm通常会用--preserve-root保护最直接的rm -rf /,但rm -rf /*、关闭保护机制或其他错误路径仍然可能造成灾难性删除。不要把这层保护当成保险。 - 通配符的空格灾难:
如果你想删除当前目录下的*(所有文件),但手抖多打了一个空格:rm -rf / home/user/docs/*(注意/后面的空格)
Shell 会把/和home/user/docs/*解析成同一条rm命令的多个删除目标,而不是拆成两条rm命令。这依然极其危险:rm会同时尝试处理根目录和另一个意外路径。 - 加一层保险 (
-i):
如果你对要删除的文件拿不准,可以使用-i(interactive - 交互式) 参数。它会在删除每一个文件前停下来问你,按y确认,按n跳过:rm -i important_script.sh
mkdir指令
mkdir 是 Make Directory(创建目录)的缩写。它是 Linux 和各类 Unix 系统中专门用于创建新文件夹的指令。
1. 基本语法
mkdir [选项] 目录名称
2. 核心用法与场景
场景一:创建单个或多个基础目录
最基础的用法,直接在当前路径下新建文件夹。
创建一个名为 src 的目录
mkdir src
同时平级创建多个目录(用空格隔开)
mkdir bin lib include
结合大括号批量创建有规律的目录(例如创建 day1 到 day5 文件夹)
mkdir day{1..5}
场景二:一键创建多层嵌套目录(最核心参数 -p)
在项目开发中,我们经常需要一次性建立深层级的目录结构(例如 src/main/cpp)。如果逐层创建会非常繁琐,且如果父目录不存在,直接创建子目录会报错。
此时必须使用 -p (parents) 参数。它的作用是:如果路径中的父目录不存在,就自动一并创建;如果目录已经存在,则直接跳过,不会报错。
即使 src 和 main 都不存在,也会一口气建好整条路径
mkdir -p src/main/cpp
常用于一键生成项目脚手架骨架
mkdir -p project/{src,bin,doc,build}
(注:上面的第二条命令会生成一个 project 文件夹,里面同时包含 src、bin、doc 和 build 四个子文件夹。)
3. 进阶选项参数
除了绝对主力 -p 之外,还有两个偶尔会用到的辅助参数:
-
-v(verbose - 详细模式):每次成功创建目录时,在终端打印一条确认信息。通常和-p结合使用,让你看清楚系统到底帮你建了哪些层级。mkdir -pv app/config/json 输出示例: mkdir: 已创建目录 'app' mkdir: 已创建目录 'app/config' mkdir: 已创建目录 'app/config/json' -
-m(mode - 设置权限):在创建目录的同时,直接指定该目录的权限(类似chmod的数字用法),而不是使用系统的默认权限。创建一个目录,并将其权限直接设定为 777(所有人可读、可写、可执行) mkdir -m 777 shared_folder
总结
对于日常操作,牢记以下两个准则即可:
- 建平级文件夹:直接
mkdir 名字。 - 建带路径的深层文件夹:永远加上
-p,即mkdir -p 路径。
rmdir指令
既然咱们前面讲过创建目录的 mkdir,以及能毁天灭地的 rm -r,那确实该给文件操作补上最后一块拼图:rmdir。
rmdir 是 Remove Directory(移除目录)的缩写。
它的核心作用和它最大的特点可以用一句话概括:它只能、且绝对只能删除“空文件夹”。
1. 基本语法
rmdir [选项] 目录名称
2. 核心用法与实战场景
场景一:安全删除空目录(最基础用法)
假设你建了一个 test_folder,里面什么都没放,现在不想要了:
rmdir test_folder
🚨 经典报错场景:
如果这个文件夹里哪怕只有一个文本文件,或者一个你看不见的隐藏文件(比如 .git 或 .DS_Store),rmdir 都会立刻拒绝执行并报错:
rmdir: failed to remove 'test_folder': Directory not empty
(这正是它的核心设计哲学:绝对防呆,绝不误杀。)
场景二:像剥洋葱一样删除嵌套的空目录(参数 -p)
还记得 mkdir -p a/b/c 可以一口气建好几层文件夹吗?rmdir 也有一个对称的 -p (parents) 参数。它的逻辑是:先删最底层的 c,如果删完后 b 也空了,就把 b 也删了;如果 b 删完后 a 也空了,就把 a 也连锅端掉。
假设 a 里面只有 b,b 里面只有 c,且 c 是空的。
这条命令会把 a, b, c 三层空文件夹全部清理干净。
rmdir -p a/b/c
(注:如果在删到 b 的时候,发现 b 里面除了 c 还有一个 config.txt,那么 rmdir 会安全停手,保留 a 和 b,并提示你 b 不为空。)
3. 开发者灵魂拷问:既然有 rm -rf,还要 rmdir 干嘛?
很多新手在学会了无敌的 rm -rf 后,就再也不碰 rmdir 了,觉得它又弱又啰嗦。但作为严谨的 C++ 开发者或系统运维,你应该在合适的场景重新启用它。
核心理由:安全容错(Fail-Safe)
想象你在写一个自动部署脚本,脚本的最后一步需要清理刚才临时创建的下载目录 temp_build。
- 如果写
rm -rf temp_build:
万一前面某一步逻辑出错了,导致系统把一些极其重要的核心代码错误地输出到了temp_build里,rm -rf会不眨眼地把它们全部扬了。 - 如果写
rmdir temp_build:
如果一切按计划进行,临时文件已经被程序移走了,temp_build是空的,它会被安全删除。如果出了意外,里面还有遗留的重要文件,rmdir会报错并停止,把现场完好无损地保留下来让你排查 Bug。
💡 极客组合技:一键清理项目中所有的废弃空目录
结合“硬盘雷达” find 指令,你可以写出一行极其优雅的代码,帮你把庞大项目里那些因为重构代码而遗留下来的、毫无用处的空文件夹全部揪出来并安全删掉:
从当前目录(.)开始,寻找类型为目录(-type d)且为空(-empty)的文件夹,并将它们删除(-delete)
find . -type d -empty -delete
(这个组合比满世界敲 rmdir 要高效得多!)
man 指令
man / info 主要查 外部命令或系统手册
man 是 Manual(手册)的缩写。在 Linux 和类 Unix 系统中,它是你最可靠、最权威的“系统说明书”。
当遇到不熟悉的指令、想查某个参数的用法,或者想了解底层 C 语言系统调用的细节时,第一时间就应该呼叫 man。
1. 核心作用
提供系统内置的详细文档。它不仅能查命令行指令,还能查配置文件格式、甚至 C/C++ 的库函数和系统调用。
2. 基本语法
man [选项] [章节号] 查询词
3. 基础用法与快捷操作
最直接的用法就是跟上你想查的指令名称:
man ls
执行后,终端会进入一个专门的阅读界面(通常由 less 程序驱动)。在这个界面中,你需要掌握以下几个必备快捷键:
q:退出(Quit)。这是最重要的一点,阅读完毕后按q就能退回到正常终端。Space(空格键):向下翻一页。b:向上翻一页(Back)。/关键字:向下搜索特定内容。例如输入/recursive然后回车,系统会高亮该单词。按n找下一个,按N找上一个。h:呼出帮助菜单,查看更多快捷键。
4. 进阶用法:手册的“章节” (开发者必知)
手册的内容非常庞大,为了避免重名冲突,系统通常将文档划分为多个“章节”(常见是 1~9,不同系统可能会有差别)。这对进行底层开发尤其重要。
如果不加章节号,man 会默认从第 1 章开始按顺序查找,找到第一个就停下。但有时候,同一个词在不同章节有不同含义。
最核心的几个章节号:
- 1:用户命令(日常敲的指令,如
ls,cd,rm) - 2:系统调用(内核提供的函数,如
fork,read,write) - 3:C 语言库函数(如
printf,malloc,strlen) - 4:特殊文件/设备相关说明(很多系统会把
/dev下设备文件的说明放在这里) - 5:配置文件格式(如
/etc/passwd的格式说明) - 6:游戏等程序的手册
- 7:杂项、约定、协议等说明
- 8:系统管理命令,很多内容主要面向管理员
- 9:内核相关内容(是否提供以及具体内容取决于系统)
实战对比场景:printf
如果你直接输入:
man printf
它会默认打开第 1 章,告诉你如何在 Shell 脚本中使用 printf 指令。
但如果你在写 C/C++ 代码,想查 <stdio.h> 里面的 printf 函数的标准用法,你需要明确指定看第 3 章:
man 3 printf
同样的道理,如果你想查 C 语言里的 sleep 函数,应该用 man 3 sleep;查操作系统的系统调用,可以用 man 2 open。
5. 其他实用辅助参数
-a(all):同一个名字如果在多个章节中都有手册,依次显示所有匹配项,而不是找到第一个就结束。man -a printf-k(keyword - 模糊搜索):如果你忘了具体的指令名字,只记得相关的功能关键字,可以用它来搜索所有相关的手册。man -k "network topology" # 会列出所有描述中包含该关键字的指令和函数-f(等同于whatis):如果你不想看长篇大论,只想知道这个指令“用一句话概括是干嘛的”。man -f touch # 输出可能只有一行:touch (1) - change file timestamps
补充:路径最后一个文件是目录文件可以在整个路径结尾处加/代表最后一个文件是目录?
1.加 / 代表它是一个目录?➡️ 对的!
在 Linux 中,路径末尾的 / 确实是专属目录的“身份标识”。
- 如果你写
dir/,系统会强制把它当成一个目录来处理。 - 如果这是一个普通文本文件,但你非要在后面加个斜杠(比如
touch my_text.txt/),系统会直接报错:Not a directory(不是一个目录)。
所以,加 / 是一种非常好的、明确表达语义的方式。
2. 加不加都完全等价?➡️ 错!在很多场景下,它们天差地别!
虽然在 cp 或者 mv 命令里,src 和 src/ 恰好表现得一样,但这只是个例。在 Linux 的世界里,带不带尾部斜杠,很多时候会导致完全不同的执行结果。
💡 极简操作法则(开发者生存指南)
为了避免踩坑,建议你养成这样的肌肉记忆:
- 补全原则:敲击
Tab键时系统自动加上的/,如果是在做cd、ls、cp、mv操作,直接保留,不用刻意删掉。 - 操作对象原则(需要补充限定):
- 在
rsync等少数对尾部/有专门语义的命令里,是否加/确实可能决定是“目录本身”还是“目录内容”; - 但不能把它推广成所有 Linux 命令的统一规则。
cd、ls、cp、mv、rm、rsync对尾部/的处理各有差异,实际以当前命令的语义为准。
- 在
- 软链接警惕:用
rm删除软链接时,绝对不要手贱在后面补/。
cp 指令
cp 是 Copy(复制)的缩写。在 Linux 环境中,它的核心作用是将文件或目录从一个地方复制到另一个地方。
它的核心原则就是 “克隆”。它的底层动作仅仅是读取源文件的数据,然后在目标位置全新写入一份一模一样的数据。在这个操作的整个生命周期中,源文件自始至终都不会被抹除、清空或修改。 执行完 cp 指令后,必然是源文件和目标文件同时存在(变成了两份独立的数据)。
与 Windows 下的 Ctrl+C 和 Ctrl+V 不同,命令行的 cp 可以在复制的同时直接完成重命名,这让它在备份配置文件等场景下极为高效。
1. 基本语法
cp [选项] 源文件 目标路径或目标文件名
2. 核心用法与场景
场景一:复制普通文件(基础用法)
如果不加任何参数,cp 只能用来复制普通文件。
1. 复制文件到另一个目录(保持原名)
# 将 main.cpp 复制到 backup 文件夹中
cp main.cpp backup/
2. 原地复制并重命名(最常见:备份配置文件)
# 复制一份 nginx.conf 并命名为 nginx.conf.bak
cp nginx.conf nginx.conf.bak
3. 复制到另一个目录并重命名
# 将 index.html 复制到 /tmp 目录下,并改名为 test.html
cp index.html /tmp/test.html
4. 同时复制多个文件到一个目录
cp file1.txt file2.txt file3.txt target_folder/
场景二:复制目录(必须加上 -r)
和 rm 删除目录的逻辑一模一样,如果你想复制一个完整的文件夹,不能直接用 cp,必须加上 -r (recursive - 递归) 参数。它会连同文件夹里面的所有子文件和子目录一起打包复制。
将整个 src 目录及其内部所有内容复制到 backup_src 目录中
cp -r src/ backup_src/
场景三:保留文件原始属性(加上 -p)
在 C++ 开发或系统运维中,文件往往有特定的权限(比如可执行权限)或特定的修改时间。默认的 cp 会把新拷贝出来的时间戳变成“现在”,并且可能会改变文件的所属权。
如果你想做到 “原封不动、一模一样地克隆” ,需要使用 -p (preserve - 保留) 参数。
# 复制脚本文件,并保留其原始的修改时间、权限(rwx)等属性
cp -p run_server.sh /deploy/
3. 开发者必知的避坑指南
-
默认静默覆盖,极其危险!
如果目标路径下已经存在一个同名文件,原生的cp会不作任何提示,直接覆盖它。你的旧数据会瞬间丢失。
解决办法:养成习惯,对重要文件使用-i(interactive - 交互式) 参数。如果遇到同名文件,系统会停下来问你是否覆盖。cp -i config.json /etc/ # 如果 /etc/ 下已有 config.json,会提示:cp: overwrite '/etc/config.json'?(注:有些发行版或用户配置会额外给
cp设置交互式别名,例如某些环境下 root 的cp可能被设成cp -i;但这不是所有 CentOS/Ubuntu 环境都统一保证的。写脚本时不要依赖这种别名保护。) -
-v(verbose - 详细模式)
当你在复制一个含有成千上万个文件的巨大项目(比如从外部 U 盘拷入整个库源文件)时,终端可能会长时间没有明显反馈。加上-v可以实时打印正在复制的文件名/路径,让你知道命令仍在工作;它并不是带百分比的真正“进度条”:cp -rv my_huge_project/ /mnt/backup/ -
-i或–interactive 覆盖⽂件之前先询问⽤⼾ -
-f或--force:需要时强制覆盖目标;但它仍然受文件系统权限限制,并不是“无视权限”。
总结
- 复制文件:直接
cp 源文件 目标位置 - 复制目录:用
cp -r 源目录 目标位置 - 备份改名一步到位:
cp 文件名 文件名.bak
mv指令
mv 是 Move(移动)的缩写。它是 Linux 系统中用于移动或重命名 文件和目录 的指令。
你可以把它等同于 Windows 里的 “剪切 + 粘贴” 或者是 “重命名” 操作。
1. 基本语法
mv [选项] 源文件 目标路径或目标文件名
2. 核心用法与场景
mv 的行为完全取决于你给定的目标位置是什么状态。它最常用于以下三种场景:
场景一:原地重命名(最常用场景之一)
如果你将文件移动到“当前目录”,并给了一个新的名字,那它实际上就是重命名。在重构 C++ 项目代码时,这个操作非常高频。
将 old_class.cpp 重命名为 new_class.cpp
mv old_class.cpp new_class.cpp
将一个名为 temp 的文件夹重命名为 build
mv temp build
场景二:移动到另一个目录(纯移动)
如果目标位置是一个已经存在的目录,mv 会把源文件“剪切”到那个目录里,保持文件名不变。
将 main.cpp 移动到 src 目录下
mv main.cpp src/
同时移动多个文件(将三个头文件全部移动到 include 目录下)
mv config.h utils.h math.h include/
场景三:移动并同时重命名
如果目标位置是一个带有新文件名的路径,mv 会在移动的同时顺便把名字改了。
# 将 main.cpp 移动到 backup 目录下,并改名为 main_bak.cpp
mv main.cpp backup/main_bak.cpp
3. 开发者必知的核心差异与避坑
💡 重点差异:移动目录不需要 -r
还记得前面讲过的 rm (删除) 和 cp (复制) 吗?如果要操作整个文件夹,它们都必须加上 -r 参数进行递归操作。
但 mv 不需要。 移动文件夹和移动普通文件的命令完全一样:
# 直接把整个 lib 文件夹移动到 vendor 目录下,不需要 -r
mv lib/ vendor/
🚨 危险警告:默认静默覆盖
和 cp 一样,如果目标位置已经存在一个同名文件,原生的 mv 会直接覆盖它,没有任何提示,旧数据会瞬间蒸发。
-i(interactive - 交互式):安全第一。如果你不确定目标目录里有没有同名文件,加上它。系统会在覆盖前停下来问你:mv -i new_config.json /etc/config.json # 如果已存在,会提示:mv: overwrite '/etc/config.json'?-n(no-clobber - 不覆盖):如果目标位置已有同名文件,直接跳过,不执行移动,也不报错。这在批量移动文件时非常有用。-f(force - 强制):无视任何阻拦,强制覆盖(通常在写自动化脚本时使用)。
4. 底层机制揭秘(为什么 mv 这么快?)
如果你要复制 (cp) 一个 10GB 的大文件夹,可能需要等好几分钟,因为系统要把磁盘里的数据块一块一块地复制过去。
但是,如果你要移动 (mv) 这个 10GB 的文件夹到同盘的另一个位置,它几乎是瞬间完成的。
为什么?
在 Linux 的底层文件系统(如 ext4)中,文件由“目录项(dentry,记录文件名)”和“索引节点(inode,指向磁盘上真正的物理数据块)”组成。
当源和目标位于同一个文件系统时,mv 通常可以通过重命名/调整目录项完成,因此不需要搬动那 10GB 的文件内容。它主要是在文件系统命名关系上完成移动,而不是重新复制整份数据。
(注:如果源和目标跨文件系统,mv 无法只靠一次重命名完成,用户层面的效果通常类似“先复制到目标,再删除源”。是否跨磁盘/分区并不是唯一判断标准,关键是是否跨文件系统。)
cat指令
cat 是 Concatenate(连接/拼接)的缩写。虽然它的英文本意是“拼接文件”,但在日常开发和运维中,它最核心、最高频的作用其实是:快速查看文件内容。
你可以把它想象成一个“无情的文件内容倾倒机”,它会把你指定的那个文件里的所有文字,一口气全部倒在你的终端屏幕上。
1. 基本语法
cat [选项] 文件名
•-b 对⾮空输出⾏编号,空⾏不做编号
•-n 对输出的所有⾏编号
•-s 不输出多⾏空⾏(即遇到多个连续空行,只输出一个空行,多余空行被删除)
2. 核心用法与场景
场景一:快速查看文本文件(最绝对的主力用法)
当你只想看一眼配置文件或短小的代码文件,不想慢吞吞地打开 vim 或 nano 编辑器时,cat 是最快的选择。
# 查看 CMakeLists.txt 的全部内容
cat CMakeLists.txt
场景二:带行号查看(加上 -n)
在和同事核对报错日志或者代码逻辑时,行号极其重要。-n (number) 参数会为每一行(包括空行)自动加上编号。
# 查看 C++ 源文件并在最左侧显示行号
cat -n main.cpp
场景三:回归本意 —— 拼接多个文件
这是 cat 真正的老本行。它可以把多个文件首尾相连,并结合重定向符号 >,输出成一个全新的文件。
# 把 header.txt 和 body.txt 的内容拼接起来,生成一个新的 full_page.txt
cat header.txt body.txt > full_page.txt
场景四:黑客风的“快速写入”
如果你想飞速创建一个新文件并写几句话,不想打开任何编辑器,可以利用 cat 和重定向:
cat > test.txt
敲下回车后,光标会闪烁等待你输入内容。你可以在屏幕上随意打字,敲完之后,按下键盘上的 Ctrl + D(代表 EOF,文件结束),你刚才打的字就全部存进 test.txt 里了。
3. 🚨 开发者避坑指南(极其重要)
在服务器上查日志时,cat 有一个致命的弱点。
千万不要用 cat 去看巨大的文件!
如果你手滑对一个 2GB 的 Nginx access.log 日志文件敲了 cat,它会疯狂地往你的终端屏幕上打印内容,整个终端窗口会瞬间被滚动的字符卡死(俗称“刷屏”),你连按 Ctrl+C 强行中断可能都要卡半天。
💡 终极替代方案(按文件大小区分):
- 对于小文件(几十行到几百行):放心用
cat。 - 对于大文件(你想分页慢慢看):必须用
less。
(less big_logfile.logless只会加载当前屏幕显示的内容,按空格往下翻页,按q退出,和man手册的操作一样,极度安全且省内存。) - 只想看大文件的最后几行(比如看最新的报错日志):必须用
tail。# 只看最后 20 行日志 tail -n 20 error.log # 实时监控滚动日志(极其高频!) tail -f error.log
tac指令
既然咱们之前讲过了 cat(顺序打印文件),那就必须得提一提它的“镜像兄弟” —— tac。
你仔细看这个名字,它就是 cat 这三个字母倒过来拼写!
它的核心作用也完全符合它的名字:按行倒序输出文件内容(最后一行最先显示,第一行最后显示)。
1. 核心概念演示
假设你有一个名为 demo.txt 的文件,内容如下:
第 1 行:程序启动
第 2 行:正在连接数据库...
第 3 行:Error: 数据库连接失败!
- 如果你用
cat demo.txt,输出顺序就是 1 -> 2 -> 3。 - 如果你用
tac demo.txt,输出顺序就会变成:第 3 行:Error: 数据库连接失败! 第 2 行:正在连接数据库... 第 1 行:程序启动
2. 开发者最爱的高频实战场景:日志逆向排查
你可能会问:既然有 tail 可以看日志尾部,要 tac 干嘛?
tac 真正的威力,在于它配合管道符 | 进行“最新状态”的过滤分析。
因为服务器日志永远是追加写入的(最新的日志在最下面)。如果你用 cat 配合 grep 搜报错,它会从文件最开头开始搜,把几年前的陈年老 Bug 最先列出来给你看。
🌟 杀手级组合技:找出“最新”的 5 个报错信息
- 错误思路(找出来的是最老的):
cat error.log | grep "Exception" | head -n 5 - 正确思路(瞬间找到最新的,因为从后往前搜):
# 先用 tac 把文件首尾翻转,再用 grep 过滤,最后截取前 5 行 tac error.log | grep "Exception" | head -n 5
这是排查线上 C++ 服务崩溃日志时,极其优雅且高效的黑客技巧。
3. 💡 极客冷知识:tac 与 rev 的区别
很多新手容易混淆 Linux 里的两个翻转指令,作为开发者你需要精确区分:
tac(按行垂直翻转):
保持每一行里面的文字顺序不变,只是把行与行的顺序上下颠倒。(最常用)rev(Reverse - 按字符水平翻转):
保持行与行的顺序不变,但是把每一行里面的字左右完全颠倒!- 比如原文件有一行是
hello world。 - 经过
rev处理后,会输出dlrow olleh。
(注:rev通常用于非常底层的字符串处理脚本,或者做密码学/字符混淆实验时使用,日常排查很少用到。)
- 比如原文件有一行是
echo指令
如果说之前讲的 cat 是往外倒文件内容的“倾倒机”,那么 echo 就是 Linux 里的 “扩音器”。
它的核心作用极其单纯:你让它说什么,它就在终端屏幕上打印什么。
1. 基本语法
echo [选项] [字符串或变量]
2. 核心用法与实战场景
场景一:单纯打印文本(最基础)
echo "Hello, Linux!"
# 终端会输出:Hello, Linux!
(注:绝大多数情况下,加不加双引号都可以,但如果文本里有空格或特殊字符,强烈建议加上双引号。)
场景二:⭐ 结合重定向快速写文件(开发者极高频用法)
还记得之前讲 cat > test.txt 可以快速写文件吗?echo 配合重定向符号(> 和 >>),是自动化脚本中生成配置文件或追加日志的终极武器。
- 单大于号
>(覆盖写入):如果文件存在,会清空原内容;不存在则创建。# 快速创建一个包含 C++ 基础框架的源码文件 echo "int main() { return 0; }" > main.cpp - 双大于号
>>(追加写入):在文件末尾继续追加输出,不会先清空原有数据。>>本身不会自动保证“另起一行”,是否出现换行取决于原文件末尾和本次输出内容中是否带有换行符。# 往配置文件末尾追加一行新的 IP 地址配置 echo "192.168.1.100 server_node" >> /etc/hosts
场景三:查看系统环境变量(必须掌握)
在 C++ 或单片机开发中,经常需要配置编译器路径。想要知道当前系统究竟配置了哪些路径,或者查看某个特定的变量值,echo 是唯一的首选:
# 查看最重要的环境变量 PATH(决定了系统去哪里找命令)
echo $PATH
# 查看 Shell 环境变量中记录的用户名(多数普通登录场景与 whoami 相同,但 su/sudo 等情况下可能不同)
echo $USER
# 查看当前用户的家目录路径(等价于 cd ~ 然后 pwd)
echo $HOME
(
注:Linux 中,在单词前面加上$符号,就代表引用这个变量的值。)
场景四:转义字符的魔法(参数 -e)
原生的 echo 是不懂换行或制表符的。如果你希望它能识别像 \n (换行) 或 \t (Tab 缩进) 这样的 C 语言风格转义字符,必须加上 -e (enable interpretation of backslash escapes) 参数。

💡 极客冷知识:清理文件的“优雅”方式
当你面对一个 10GB 的巨型日志文件,想要瞬间清空它但又保留这个文件本身(不要删掉重建,以免破坏正在写日志的程序的句柄),千万不要用 rm。
你可以用 echo 瞬间完成“洗白”:
# 将文件直接截断为 0 字节,不删除文件本身
> huge_error.log
echo "" > huge_error.log 通常会写入一个换行符,因此文件一般不是 0 字节;如果目标就是清空到 0 字节,直接使用上面的 > huge_error.log 更准确。
补充:重定向
既然前面讲 echo 的时候已经瞥见了 > 和 >> 的身影,那咱们就顺势把 Linux 中极其核心的 “重定向(Redirection)” 机制彻底讲透。
要理解重定向,首先要建立一个心智模型:把 Linux 的指令想象成一个“水泵”,而数据就是“水流”。
默认情况下,这个水泵从“键盘”抽水(读取输入),然后把水排到“屏幕”上(打印输出)。
重定向的本质,就是你手动接了几根水管,强行改变了水流的方向。
在深入语法之前,作为开发者,你必须先认识 Linux 的三个默认“水管接口”(文件描述符):
0(Standard Input - stdin):标准输入(默认是键盘)1(Standard Output - stdout):标准输出(默认是屏幕的正常显示)2(Standard Error - stderr):标准错误输出(默认也是屏幕,但专门用来显示报错信息)
1. 输出重定向(改变排出的水流)
这是开发中最常用的操作,主要是把原本要打印在屏幕上的日志或结果,存入硬盘文件里。
单大于号 >:覆盖输出 (Overwrite)
动作:如果文件不存在则创建;如果文件已存在,毫不留情地清空原有内容,然后写入新数据。
# 将 ls 的正常输出结果,写入文件
ls -lah > dir_list.txt
# 将编译输出重定向(每次编译前都会覆盖旧的 build.log)
g++ main.cpp > build.log
双大于号 >>:追加输出 (Append)
动作:如果文件不存在则创建;如果已存在,则从文件末尾继续追加输出,不会先清空旧数据。>>本身不保证“另起一行”,换行由实际输出内容决定。
# 将一条新日志追加到现有日志文件末尾
echo "Server started at $(date)" >> server.log
2. 输入重定向(改变抽水的水源)
输入重定向让命令不再等待你在键盘上敲字,而是直接从文件里“吸”数据。
单小于号 <:输入重定向
动作:把文件内容作为数据流,喂给前面的命令。
# 统计行数指令 wc (word count)。-l 表示统计行数
# 写法A:把文件作为参数传给它
wc -l config.json
# 输出: 120 config.json (它知道文件名字)
# 写法B:使用输入重定向,把内容“灌”给它
wc -l < config.json
# 输出: 120 (它根本不知道水是从哪个文件来的,只负责数流过了多少行)
双小于号 <<:嵌入式多行输入 (Here Document)
动作:之前讲的单小于号 <,是把一个已经存在的、写好的文件灌给命令。但很多时候,我们不想专门去建一个文本文件,而是想直接在写脚本的时候,临时手敲一大段包含回车换行的文字喂给命令。<< 就是用来干这个的,它相当于在命令行里临时切出了一个“多行文本输入框”。它不是从现有的文件里读取,而是允许你在命令行(或脚本)里连续输入多行文本,直到你敲出一个预设的“结束符”(通常约定俗成用 EOF,即 End Of File)为止,然后再把这一大块文本一并喂给命令。
实战场景:在脚本中自动生成多行配置文件
命令 << 结束标记
多行内容
多行内容
结束标记
遇到 EOF 前的所有内容,都会被当作输入喂给 cat,最后再通过 > 写入 nginx.conf
cat << EOF > nginx.conf
server {
listen 80;
server_name localhost;
root /var/www/html;
}
EOF
(当你敲下最后一个 EOF 并回车时,整个多行配置就被瞬间写好了,这在部署脚本中是极其优雅的写法。)
3. 🚨 开发者进阶:分离与合并“正常输出”与“报错信息”
这是 C++ 开发和服务器运维中最容易踩坑,也是最能体现专业度的地方。
先建立核心心智模型:两根排血管
当你在终端运行任何一个命令(比如编译代码 g++ main.cpp),这个程序其实有两根向外喷水的管子,默认情况下,这两根管子都对准了你的“屏幕”:
- 1号水管(标准输出 stdout):只负责吐出正常的执行结果、提示信息。
- 2号水管(标准错误 stderr):专门负责吐出报错、警告、异常信息。
为什么你会踩坑?
当你执行 g++ main.cpp > build.log 时,如果代码有错,你会发现报错信息依然在屏幕上刷屏,而 build.log 里面空空如也!
原因在于: 我们平时用的单大于号 >,其实是 1> 的简写。它只移动了 1号水管,把它接到了文件上。而喷吐错误的 2号水管,依然对准着你的屏幕!
为了精准控制这两根水管,我们有以下三种常见实战场景:
场景 A:我只要错误日志(正常信息留屏幕,报错存文件)
有时候程序正常运行的日志太多了,我们不关心,我们只想把报错的信息单独存下来慢慢看。
这里的 2> 就代表你明确抓住了“2号水管(报错)”,把它插进 error.log 文件里。
至于 1号水管(正常信息),没碰它,它还是正常输出在屏幕上。
g++ main.cpp 2> error.log
场景 B:将两根水管合并(极其高频!正常和报错全都要)
这是最常用的场景:你想把正常输出和错误报错,按顺序完整地存进同一个文件里,方便事后复盘排查。
写法 1:老手常用的标准写法(理解原理)
g++ main.cpp > full_build.log 2>&1
这堆乱码一样的符号 > full_build.log 2>&1 到底在干嘛?我们分两步看:
> full_build.log:先把 1号水管 接到了full_build.log文件上。2>&1:把 2号水管 接到 1号水管 现在的通道里。(这里的&是关键,它告诉系统“这里的 1 指的是 1号水管,而不是一个名字叫作1的文件”)。
结果就是:两根水管汇流了,统统流进了一个文件里。
写法 2:现代 Bash 的极简写法(强烈推荐,小白福音)
这里的 &> 是一个组合技,意思是:“别管 1号还是 2号了,把所有向外的水管全部给我绑在一起,塞进后面的文件里!”
g++ main.cpp &> full_build.log
场景 C:丢进黑洞(眼不见为净)
Linux 里有一个神奇的虚拟设备文件叫做 /dev/null。你可以把它当成宇宙中的黑洞、或者一个绝对填不满的垃圾桶。任何扔进它的数据都会被瞬间销毁,不占任何硬盘空间。
当你运行一个极其啰嗦的程序,且你百分之百确定你不需要看它的任何输出时:
把所有水管(&>)全部对准黑洞,你的终端屏幕将绝对清净,也不会生成任何垃圾文件
./run_noisy_server &> /dev/null
补充:管道|
如果说重定向是把水管接到了 “水桶(文件)” 里,那么管道符 | (键盘回车键上方的那个竖线),就是把水管接到了 “下一台处理机器(另一个指令)” 上。
这是 Linux 哲学中最伟大、最核心的设计:“让每个程序只做好一件事,然后把它们串联起来完成复杂的工作。”
1. 核心概念与比喻
动作:将左边命令的标准输出(stdout),直接作为右边命令的标准输入(stdin)。
比喻:想象一条工厂流水线。
- 机器 A 负责把苹果从树上摇下来(输出大量数据)。
- 机器 B 负责把烂苹果挑出去(过滤数据)。
- 机器 C 负责把好苹果装箱打包(截取或统计)。
如果没有管道,你得先用机器 A 把苹果全堆在地上(写进文件),再让机器 B 从地上捡起来挑……有了管道,机器 A 的出口直接连着机器 B 的入口,数据在空中不落地,瞬间完成处理!
2. 基本语法
指令A | 指令B | 指令C ...
(你可以无限量地串联下去,只要前一个指令有输出,后一个指令能接收输入。)
3. 开发者最高频实战场景
其实我们在前面讲解其他指令时,已经悄悄用过管道了。现在我们系统地把它们组合起来:
场景一:配合 grep 变身“超级过滤器”
这是每天都要敲无数遍的组合。
1. 在密密麻麻的系统进程中,找出名叫 nginx 的进程
ps aux | grep nginx
2. 从历史记录里,找出所有关于 docker 的操作
history | grep docker
3. 看看系统安装了哪些包含 "python" 的软件包(以 Ubuntu 为例)
dpkg -l | grep python
场景二:配合 less 或 more 防止“刷屏爆炸”
当左边的命令会吐出成千上万行数据时,用管道把它喂给 less,慢慢翻页看。
# 查看 /etc 目录下所有文件及其详细权限,不刷屏
ls -lah /etc | less
场景三:配合 wc 变身“统计神器”
这里引入一个新指令:wc (Word Count)。如果加上 -l (lines) 参数,它就是专门用来“数行数”的。
组合技:统计当前目录下到底有多少个文件和文件夹?
ls -1 | wc -l
(ls 吐出文件列表,wc 把吐出来的行数数一遍)
组合技:统计错误日志里,到底出现了多少次 "Error"?
cat system.log | grep "Error" | wc -l
场景四:组合截取(Head / Tail)
# 组合技:查看当前目录下占用空间最大的前 5 个文件
ls -lS | head -n 5
# (ls -lS 按大小倒序输出,交给 head 只截取前 5 行)
4. 🚨 核心辨析:管道 | vs 重定向 >
这是新手最容易混淆的地方,务必牢记:
-
重定向>:左边是指令,右边必须是文件。- ❌
ls > grep(大错特错,这会创建一个名叫 “grep” 的文件,把文件列表写进去!) - ✅
ls > file.txt
- ❌
-
管道|:左边是指令,右边必须也是指令(且必须是能接收输入的指令)。- ❌
ls | file.txt(大错特错,“file.txt” 不是个能执行的命令,系统会报错command not found) - ✅
ls | grep "txt"
- ❌
💡 极客终极组合:又想接管子,又想存文件(tee 指令)
有时候你会遇到这样一个需求:流水线上的数据,我既想让它继续传给下一台机器,又想在这个环节 “分流” 一份存进日志里备份。
这时候就要用到 T型水管:tee。
把系统日志交给 grep 过滤,过滤出的结果不仅显示在屏幕上,还同时保存进 error_backup.log 文件里
cat /var/log/syslog | grep "ERROR" | tee error_backup.log
掌握了管道,你就从“只会敲单个命令的 Linux 用户”,正式进阶为“能像搭积木一样操控系统的 Linux 开发者”了。
more指令
more 是 Linux 中非常经典的分页阅读器(Pager)。
还记得前面提到 cat 遇到大文件会疯狂刷屏卡死终端吗?more 就是为了解决这个问题而诞生的。它的核心作用是:把长篇大论的文件内容,一页一页(一屏一屏)地展示给你看。
1. 基本语法
more [选项] 文件名
-n指定输出⾏数( more -10 temp.txt)
2. 必备的阅读快捷键(进去后怎么操作)
当你敲下 more 并回车后,终端底部会出现一个类似 --More--(15%) 的进度提示。此时,你需要使用以下按键来控制阅读:
Space(空格键):向下翻一页。(这是最高频的按键)Enter(回车键):向下翻一行。b:向上翻一页(Back)。/关键字:向下搜索某个词。q:退出(Quit),回到正常的命令行终端。
3. 开发者最高频的实战场景:结合“管道符 |”
在实际开发中,直接 more 文件名 并不算最多,more 最强大的用法是作为拦截器,拦截其他命令的巨量输出。
这里要介绍 Linux 中极其重要的管道符 |(就是把前一个命令的输出,当成水流一样,导入到下一个命令里)。
场景一:查看目录里海量的文件
如果一个文件夹里有 2000 个文件,直接敲 ls -lah 也会刷屏。你可以把它交给 more:
ls -lah /etc | more
(系统会先列出 /etc 下的文件,但不会刷屏,而是显示满第一屏就停下来,等你按空格翻页。)
场景二:翻阅历史记录
你在这个服务器上敲了 5000 条命令,想从头慢慢找:
history | more
场景三:查看进程列表
ps aux | more
4. 💡 终极极客常识:为什么老手都在用 less?
在 Linux 圈子里有一句著名的冷笑话:“less is more”(少即是多)。
这句话不仅是极简主义的口号,也是一句真实的 Linux 指南。因为最初的 more 指令设计得比较简陋,存在几个硬伤:
- 只能往下看,往回看很费劲:尤其是在结合管道符
|使用时,早期的more甚至不支持按b往回翻。 - 看完就自动退出:当你翻到文件最后一行时,
more会自动退出并把你踢回终端。有时候你只是想停在最后一行仔细看看,结果一闪而过了。但是信息会残留再屏幕上
为了解决这些痛点,开发者们写出了 less 指令。less 完全包含了 more 的所有功能,并且:
- 上下翻页极其丝滑(可以用键盘的 ↑ ↓ 箭头,也可以用 PageUp / PageDown)。
- 翻到文件末尾不会自动退出,直到你主动按
q。 - 搜索功能更强大。
总结:more 是经典的分页工具,掌握它的空格和退出键即可。但在今天,只要你的键盘能敲出 less,就尽量用 less 替代 more。
less 指令
less 是 Linux 中最强大、最优雅的分页阅读器。
它不仅完全替代了 more 的功能,而且在处理巨大的日志文件(如几十 GB 的 C++ 报错日志或 Nginx 访问日志)时,是所有开发者的首选工具。
1. 核心优势(为什么它是王者?)
- 极速加载,不吃内存:与
cat或一些文本编辑器不同,less不会在一开始就把整个文件读进内存。无论文件是 1KB 还是 100GB,它都能瞬间打开,因为它只加载当前屏幕需要显示的那一点点内容。 - 阅后即焚(不脏屏幕):当你按
q退出less时,它会把刚才显示的文件内容从终端屏幕上抹除,恢复到你输入命令前的干净状态,不会像cat或more那样留下一大串文字干扰你的视线。
2. 基本语法
less [选项] 文件名
•-i 忽略搜索时的⼤⼩写
•-N 显⽰每⾏的⾏号
3. 开发者必备的“指法”(快捷键)
打开 less 后,你的终端就变成了一个阅读器,掌握以下快捷键可以让你在代码和日志中穿梭自如:
🌟 基础翻页(丝滑上下)
Space/PageDown:向下翻一页。b/PageUp:向上翻一页(Back)。↓/Enter:向下滚动一行。↑/k:向上滚动一行。
🚀 快速跳转
g:瞬间回到文件第一行。G:瞬间跳到文件最后一行(看最新日志时极常用)。
🔍 强大的搜索功能
/关键词:向下搜索包含该词的行。- 敲击回车后,按
n(next) 找下一个,按N(大写) 找上一个。
- 敲击回车后,按
?关键词:向上搜索包含该词的行(用法同上)。
🚪 退出
q:退出阅读模式(Quit)。
4. 进阶神级用法(排查线上 Bug 必备)
场景一:显⽰每⾏的⾏号(加上 -N)
在和同事交流“某某文件第几行报错”时,行号是必须的:
less -N config.xml
场景二:充当管道过滤器(结合 |)
和 more 一样,当你执行了一个输出成千上万行的命令时,用 less 拦住它慢慢看:
# 查看所有进程,并用 less 分页,还可以使用 / 搜索特定的进程名
ps aux | less
场景三:取代 tail -f 实时看日志(极客专属:+F)
通常我们会用 tail -f error.log 来实时滚动看新产生的日志。但 less 也能做到,而且更强:
less +F error.log
- 进入后,它会像
tail -f一样实时滚动。 - 高能操作:如果此时发现了一个异常,你可以按下
Ctrl + C,它会停止滚动,让你留在原地。然后你可以用/开始搜索、用上下键翻看刚才的错误。看完了,按下F(大写),它又会恢复实时滚动模式!
head指令
既然在之前介绍 cat 的时候,咱们提到了用来查看文件尾部(最新日志)的 tail,那就必须得提它的双胞胎兄弟:head。
head 的作用非常简单直白:提取并查看文件最开头的几行内容。
当你拿到一个几十万行的巨大数据文件(比如 CSV 表格),你想看看它的表头都有哪些字段,或者你想看一个巨大的 C++ 源码文件开头包含了哪些头文件(#include),用 head 瞬间就能搞定。
1. 基本语法
head [选项] 文件名
2. 核心用法与实战场景
场景一:默认查看(最开头 10 行)
如果不加任何参数,head 默认会切出文件最前面的 10 行打印在屏幕上。
# 查看 C++ 源文件的开头 10 行(通常是版权声明或 include 列表)
head main.cpp
场景二:指定行数(最常用参数 -n)
如果你觉得 10 行不够,或者只需要看第 1 行,可以使用 -n(number)参数。
只查看文件的前 20 行
head -n 20 config.json
极简写法(省略 -n,直接写横杠加数字,老手最爱)
head -20 config.json
只看文件的第 1 行(比如提取数据表的表头)
head -1 data.csv
场景三:按字节截取(参数 -c)
除了按“行”截取,head 还可以按“字节”截取。这在处理二进制文件、或者从 /dev/urandom(Linux 系统的随机数生成器)中提取特定长度的随机密码时非常有用。
截取文件最开头的前 100 个字节
head -c 100 binary_file.bin
3. 开发者进阶用法:结合管道符 |
head 经常不单独使用,而是作为数据流的“过滤器”。
用法 A:提取其他命令输出的前几行
比如,你想看看当前目录下最大的 5 个文件是什么。你可以先用 ls 按文件大小倒序排列,然后把结果交给 head 截取前 5 个:
ls -lS | head -5
用法 B:head 与 tail 的梦幻联动(提取中间内容)
假设一个文件有 1000 行,你刚好想看第 100 行到第 120 行的内容,怎么办?
你可以先用 head 把前 120 行切下来,然后再用 tail 把这 120 行里的最后 21 行(即第 100 到 120 行)切下来:
head -120 server.log | tail -21
tail 指令
既然刚才聊完了 head(看开头),那就顺理成章地来到它的另一半——tail。
tail 的核心作用是:提取并查看文件最末尾的几行内容。
在实际的开发和运维工作中,tail 的出场率要远远高于 head。因为对于系统运行产生的日志文件来说,最新的报错和状态永远都在文件的最末尾。
1. 基本语法
tail [选项] 文件名
2. 核心用法与实战场景
场景一:默认查看(最末尾 10 行)
如果不加任何参数,tail 默认会切出文件最后面的 10 行内容。
tail server.log
场景二:指定行数(参数 -n)
和 head 一样,你可以精确指定想看最后几行。
# 查看日志的最后 50 行
tail -n 50 error.log
# 极简写法(老手最爱,直接省略 -n)
tail -50 error.log
场景三:🌟 灵魂功能 —— 实时监控(参数 -f)
这是 tail 指令最伟大、最高频的用法,没有之一。-f 代表 follow(跟随)。
当你排查程序运行状态或网络请求时,需要一边发请求,一边看后台有没有报错。此时你就可以用:
tail -f access.log
执行后,终端不会退出,而是停留在那里。只要有新的数据写入到该文件中,终端就会立刻实时滚动打印在你的屏幕上。
- 如何退出? 当你查完问题,按下键盘上的
Ctrl + C即可终止实时监控,安全退回到命令行。
场景四:从某一行开始一直到末尾(带 + 号的奇技淫巧)
有时候你可能需要跳过文件开头的配置说明,从第 100 行开始一直看到最后:
# 注意数字前面的 + 号,意思是“从第 100 行开始,一直打印到结尾”
tail -n +100 data.csv
3. 进阶避坑指南:日志轮转与大写 -F
在服务器环境中,由于日志文件会随着时间变得非常大,系统通常会使用自动工具进行“日志切割”(例如到了半夜 12 点,把 error.log 重命名为 error.log.1,然后新建一个空的 error.log 继续写)。
如果你正用 tail -f error.log 盯着屏幕,而恰好碰上了系统切割日志:
- 原生的
-f是认死理的,它在底层追踪的是文件的物理节点(inode)。一旦原文件被改名转移,它依然死死盯着那个旧文件,你将再也看不到新产生的日志,甚至会误以为系统死机了。 - 终极解决办法(
-F参数):
在看持续运行的服务日志时,养成使用 大写-F(等同于--follow=name --retry)的习惯:
这样即使旧文件被删了、改名了,只要系统重新生成了一个同名的新tail -F error.logerror.log,tail就会极其聪明地自动切换过去,继续为你实时追踪新文件。
date 指令
date 指令的核心作用非常直白:显示或设置系统的日期和时间。
在日常敲命令时,如果你只是单纯想看一眼现在几点了,直接敲 date 就能搞定。但在 C++ 开发和编写自动化 Shell 脚本时,date 的格式化输出和时间戳转换才是真正的高频考点(比如用时间给备份文件命名、计算程序运行耗时等)。
1. 基本语法
date [选项] [+格式]
2. 核心用法与开发者实战场景
场景一:直接查看当前时间(最基础)
如果不加任何参数,它会按照系统默认的时区和语言打印当前时间:
date
输出示例:2026年 04月 15日 星期三 09:54:53 CST
场景二:⭐ 自定义格式输出(写脚本绝对离不开)
这是 date 最强大的地方。date 命令里的 + 表示:后面的内容是“输出格式说明”,也就是告诉 date 按照什么格式显示日期和时间。
例如,你想生成一个名为 backup_20260415.tar.gz 的备份文件:
# 1. 最常用的标准格式 (年-月-日 时:分:秒)
date +"%Y-%m-%d %H:%M:%S"
# 输出:2026-04-15 09:54:53
# 2. 纯数字紧凑格式(常用于给文件命名)
date +"%Y%m%d_%H%M%S"
# 输出:20260415_095453
开发者必背格式符:
%Y:四位数年份(如 2026)%m:两位数月份(01~12)%d:两位数日期(01~31)%H:24小时制的小时(00~23)%M:分钟(00~59)%S:秒(00~59)%X:相当于%H:%M:%S%F:相当于%Y-%m-%d
场景三:🌟 玩转 Unix 时间戳 (Epoch Time)
在底层开发和网络请求中,我们经常使用 Unix 时间戳(从 1970 年 1 月 1 日 00:00:00 UTC 到现在的总秒数)。date 可以在人类可读时间和时间戳之间轻松转换。
1. 获取当前的 Unix 时间戳(加 %s):
date +%s
输出类似:1776218093
2. 将时间戳转换回人类可读时间(用 -d 参数):
如果你在看服务器日志时发现了一个 1776218093 的时间戳,想知道到底是几点:
注意前面要加一个 @ 符号
date -d @1776218093
场景四:时间推算(时光机功能)
当你需要排查“昨天”或“上个月”的日志时,不需要自己算闰年或者大小月,让 date -d 帮你算:
# 查看昨天的这个时候是几号
date -d "yesterday"
# 查看 3 天前是几号,并指定输出格式
date -d "3 days ago" +"%Y-%m-%d"
# 查看下个月的今天是几号
date -d "next month"
3. 补充说明:设置系统时间
虽然 date 可以用来修改系统时间,但作为普通开发者极少使用它,原因有二:
- 修改系统时间必须拥有管理员(root)权限。
- 现代的 Linux 服务器通常都运行着 NTP(网络时间协议)服务,会自动和全球标准时间服务器对时。强行手动修改可能会导致集群服务(如数据库同步、SSL 证书验证)崩溃。
(仅作了解的设置语法:sudo date -s "2026-04-15 10:00:00")
在 C++ 开发中,如果你需要计算一段程序的运行时间,通常会在 Shell 脚本里这样做:
start=$(date +%s)
# 执行你的程序 ...
end=$(date +%s)
echo "程序耗时:$((end - start)) 秒"
注意:date -d +%s是错误的
总结:
记住 date +"%Y-%m-%d %H:%M:%S" 这个标准格式串,以及 date +%s 取时间戳,这两个技能足以应付 99% 的开发场景了!
cal 指令
cal(calendar 的缩写)是 Linux 和 Unix 系统中一个非常经典且实用的命令行工具,用于在终端中输出日历。虽然功能简单,但在日常查看日期、计算时间跨度时非常方便。
以下是 cal 指令的详细讲解,包括语法、常用参数和易错点。
1. 基本语法
cal [选项] [[[日] 月] 年]
- 不加任何参数:默认显示当前系统所在月份的日历,并高亮显示今天的日期。
- 指定年份:
cal 2024(显示 2024 年全年的日历)。 - 指定年月:
cal 5 2024(显示 2024 年 5 月的日历)。 - 指定年月日:
cal 15 5 2024(显示 2024 年 5 月的日历,但这通常与仅指定年月效果相同,具体行为因系统版本而异)。
2. 常用选项(参数)
不同的 Linux 发行版(如 Ubuntu 默认的 GNU 版与 macOS 的 BSD 版)参数可能略有微调,以下是通用且最常用的参数:
-
-3(Three months)
显示前一个月、当前月和下一个月的日历。非常适合做季度规划。cal -3 -
-y(Year)
显示当前年份全年的日历。等同于cal [当前年份]。cal -y -
-m(Monday)
将星期一作为一周的第一天显示(系统默认通常是将星期日作为第一天)。cal -m -
-j(Julian days)
显示“儒略日”(Julian calendar days),即用一年中的第几天(1-365 或 366)来代替常规的 1-31 号。这在计算两个日期之间相隔多少天时非常有用。cal -j -
-w(Week number) (部分版本支持)
在日历旁边显示当前是本年度的第几周。
3. 常见用法示例
示例 1:查看当月日历
$ cal
May 2024
Su Mo Tu We Th Fr Sa
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
示例 2:查看特定月份(例如查看 2023 年 8 月)
$ cal 8 2023
示例 3:查看儒略日(看看 12 月 31 日是第几天)
$ cal -j 12 2023
December 2023
Sun Mon Tue Wed Thu Fri Sat
335 336
337 338 339 340 341 342 343
344 345 346 347 348 349 350
351 352 353 354 355 356 357
358 359 360 361 362 363 364
365
4. 新手最常见的“坑”
很多人想查看某一年 12 月的日历,会下意识地输入:
cal 12
注意:这不会显示当前年份的 12 月! 如果只提供一个数字,cal 会将其视为年份。所以 cal 12 显示的是公元 12 年的全年日历。
- 错误做法:
cal 12(查看公元 12 年) - 正确做法:
cal 12 2024(查看 2024 年 12 月)或使用-m参数(在某些发行版中cal -m 12可以表示 12月)。
5. 进阶拓展:ncal 命令
在大多数现代 Linux 系统和 macOS 中,通常还附带了另一个命令 ncal(New Calendar)。
它和 cal 类似,但排版是垂直的,星期几在左侧,日期向右延伸。
$ ncal
May 2024
Su 5 12 19 26
Mo 6 13 20 27
Tu 7 14 21 28
We 1 8 15 22 29
Th 2 9 16 23 30
Fr 3 10 17 24 31
Sa 4 11 18 25
ncal 的优势在于它支持更多的扩展功能,例如 -e(显示复活节日期)或更直观的周数显示机制。如果 cal 无法满足排版需求,可以尝试使用 ncal。
find 指令
find 它的主要作用是在指定的目录层级中实时搜索文件和目录,并且可以根据文件名、文件类型、大小、修改时间、权限等多种条件进行极其精确的查找,甚至能在找到文件后直接对它们执行特定操作。
以下是 find 指令的详细讲解:
1. 基本语法
find [查找路径] [查找条件] [处理动作]
- 查找路径:指定从哪个目录开始找。 ,如果不写,默认是当前目录(
.)。 - 查找条件:按什么规则找(例如按名字、按大小、按时间)。
- 处理动作:找到之后做什么。默认动作是打印在屏幕上(
-print),也可以是删除、复制或执行其他命令。
2. 常用查找条件(参数)
🔹 按文件名查找
-name "模式":按文件名精确查找(支持通配符*和?)。强烈建议将模式用双引号括起来,防止 Shell 提前解析通配符。-iname "模式":忽略大小写查找(i 代表 ignore case)。
🔹 按文件类型查找 (-type)
-type f:只查找普通文件 (file)。-type d:只查找目录 (directory)。-type l:只查找软链接 (link)。
🔹 按文件大小查找 (-size)
单位通常使用 c (字节), k (KB), M (MB), G (GB)。
-size +100M:查找大于 100MB 的文件。-size -10k:查找小于 10KB 的文件。-size 50M:查找刚好等于 50MB 的文件(较少用)。
🔹 按修改时间查找 (-mtime / -mmin)
这是运维和开发中最常用的功能之一。mtime 按天算,mmin 按分钟算。
-mtime +7:查找修改时间在 7天以前 的文件(即 7 天内未被修改过,通常用来清理老旧日志)。-mtime -7:查找修改时间在 7天以内 的文件(最近 7 天内修改过)。-mtime 7:查找刚好在第 7 天前修改的文件。
🔹 按深度查找(优化搜索速度)
-maxdepth n:最多搜索到第 n 层子目录。-maxdepth 1表示只在当前目录下找,不进入子目录。
3. 处理动作(进阶威力)
找到文件后,find 可以直接对它们发号施令:
-delete:直接删除找到的文件!(使用前务必先不加-delete运行一遍,确认找对文件了再加,否则后果严重)。-exec 指令 {} \;:对找到的每一个文件执行指定的 Shell 指令。{}:代表find找到的文件名。(不是必须的)\;:是-exec动作的结束标志(\; 中的 \ 是为了转义 ;,因为 ; 本身在 Shell 中表示“命令结束” 必须有分号)。
-exec 命令 {} \; # 一般:每找到一个文件执行一次
-exec 命令 {} + # 批量:一次处理多个找到的文件
\ 反斜杠:主要用于转义,如 \n、\t、\\/ 正斜杠:主要用于除法、注释以及 Linux 路径
4. 实战高频应用场景
场景 1:找遗忘的文件
“我记得有个文件名叫 config 什么的,不知道放哪了。”
# 在整个系统根目录下,查找文件名包含 config 的普通文件
find / -type f -name "*config*"
场景 2:清理老旧日志(服务器维护必备)
“找出 /var/log 目录下,所有超过 30 天未修改的 .log 文件,并直接删除。”
find /var/log -name "*.log" -type f -mtime +30 -delete
场景 3:寻找系统中的大文件(排查磁盘占用)
“当前目录下,有哪些文件超过了 500MB?”
find . -type f -size +500M
场景 4:批量修改权限
“把当前目录及所有子目录下的 HTML 文件权限改为 644。”
find . -type f -name "*.html" -exec chmod 644 {} \;
场景 5:拷贝找到的文件到指定目录
“把当前目录下所有的图片 (.jpg) 复制到 /backup/images/ 目录中。”
find . -name "*.jpg" -type f -exec cp {} /backup/images/ \;
5.find 指令顺序背后的“潜规则”:
1) 核心原理:从左到右的“短路求值” (Short-Circuit)
我们在上一节提到过,find 处理条件就像一个漏斗,从左到右依次过滤:A条件 满足 -> 继续看 B条件 满足 -> 留下来
一旦前面某个条件不满足,后面的条件就直接跳过,不再检查。
2) 测试条件(Tests)的顺序:影响效率
比如这两种写法,最终找出来的文件是一模一样的:
- 写法 A:
find . -type f -name "*.jpg"(先判断是不是普通文件,再看名字) - 写法 B:
find . -name "*.jpg" -type f(先看名字是不是 .jpg,再判断是不是普通文件)
既然结果一样,为什么要区分顺序?为了追求极致的速度!
- 假设你所在的目录有 10,000 个文件,但只有 5 个文件以
.jpg结尾。 - 如果用写法 A:系统需要对 10,000 个文件都执行一次“文件类型检查”,然后再从中挑出名字对的。
- 如果用写法 B:系统先看名字,直接就过滤掉了 9,995 个文件,只剩下 5 个文件需要进行“文件类型检查”。
- 结论:把最能大量排除干扰项的条件(通常是
-name或-maxdepth)放在最前面,可以让find跑得更快。这在遍历含有百万级文件的服务器根目录时,性能差异非常明显。
6. 新手易错点与小技巧
- “权限不够”刷屏:在非 root 用户下全局搜索时(如
find / ...),会遇到大量Permission denied错误,导致你看不清真正找到的结果。- 技巧:在命令结尾加上
2>/dev/null,将标准错误输出丢弃。 - 示例:
find / -name "mycode.py" 2>/dev/null
- 技巧:在命令结尾加上
- 忘记加引号:写成
find . -name *.txt。如果当前目录刚好有一个.txt文件,Shell 会自动将其展开,导致find实际上执行的是find . -name a.txt,从而报错或找不到其他子目录的 txt 文件。- 规范:永远写成
find . -name "*.txt"。
- 规范:永远写成
补充: find 命令中的 -o 操作符详解
-o 是 find 命令的逻辑或(OR) 操作符。它将两个条件表达式连接起来,只要其中一个为真,整个条件就为真。
1. 基本语法
find 路径 条件1 -o 条件2
等价于:条件1 OR 条件2
2. 优先级规则(非常重要)
find 的运算符优先级从高到低为:
- 括号
( ... )—— 最高优先级 -not(或!):逻辑非,优先级高于-a。-a(逻辑与) —— 默认隐含,例如-type f -name "*.txt"实际上就是-type f -a -name "*.txt"-o(逻辑或) —— 最低优先级
关键点:-a 的优先级高于 -o。
这意味着当你写下:
find /home -type f -name "*t*" -o -name "*f*"
实际解析为:
( -type f -a -name "*t*" ) -o -name "*f*"
而不是你直觉的:
-type f -a ( -name "*t*" -o -name "*f*" )
3. 常见错误示例与正确写法
❌ 错误:想找 /home 下文件名包含 t 或 f 的普通文件
find /home -type f -name "*t*" -o -name "*f*"
结果:
- 左边:普通文件且名字含
t - 右边:任何类型(包括目录、链接等)且名字含
f
→ 会列出名字含f的目录,不符合预期。
✅ 正确写法:用括号强制改变优先级
find /home -type f ( -name "*t*" -o -name "*f*" )
现在 -type f 作用于整个括号内的 OR 结果:找普通文件,并且(文件名含 t 或含 f)。
❌ 错误:想找同时包含 t 和 f 的文件(顺序不限)
find . -type f -name "*t*f*" -o -name "*f*t*"
解析为:
( -type f -a -name "*t*f*" ) -o -name "*f*t*"
→ 右边会匹配任何类型名字含 *f*t* 的条目(可能是目录)。
✅ 正确写法:
find . -type f ( -name "*t*f*" -o -name "*f*t*" )
4. -o 的短路行为
-o 遵循短路求值:
- 先评估左边的表达式。
- 如果左边为真,则不再评估右边(因为 OR 已满足)。
- 如果左边为假,才评估右边。
这可以用来避免某些操作符的副作用,比如:
find . -name "*.bak" -o -exec rm {} \;
这条命令实际上很危险,不要执行。 由于 -a 的优先级高于 -o,它会在 -name "*.bak" 为假时执行右侧的 -exec rm {} \;,也就是可能尝试删除大量不是 .bak 的匹配项。原代码保留在这里仅用于说明为什么不能凭直觉理解 -o 的短路。更安全、适合观察短路行为的例子:
find . -name "*.txt" -o -print
只打印不是 .txt 的文件(因为左边为假时才执行 -print)。
5. 混合使用 -a 和 -o
即使显式写了 -a,优先级仍然高于 -o,所以括号是必需的。
例子:查找文件大于 1M 且(文件名包含 log 或者修改时间在 7 天内)
find /var/log -size +1M \( -name "*log*" -o -mtime -7 \)
没有括号的话:
find /var/log -size +1M -name "*log*" -o -mtime -7
实际等于:( -size +1M -a -name "*log*" ) -o -mtime -7,含义完全不同。
6. 总结
-o表示 OR。-a(默认隐含)优先级高于-o。- 总是用
\( ... \)将-o两边的条件括起来,除非你完全清楚默认优先级带来的效果。 - 与
-type f等测试条件组合时,把-type f放在括号外面,让它同时作用于整个 OR 结果。
熟练使用括号后,-o 是非常清晰、强大的逻辑组合工具。
补充:深入解析 Linux 的通配符与正则表达式
在 Linux 中,通配符(Wildcards) 和 正则表达式(Regular Expressions, 简称 Regex) 是两个极易混淆的概念,因为它们不仅长得像(比如都用到了 * 和 ?),而且都用于“匹配”字符串。
但它们的核心区别在于使用场景和解析主体:
- 通配符:主要由 Shell(如 Bash) 解析,用于匹配文件或目录名。
- 正则表达式:主要由 文本处理工具(如
grep,awk,sed,vim等)解析,用于匹配文件内的文本内容或命令输出的字符串。
通配符用于文件名,正则表达式用于文件内容
一、 Linux 通配符 (Wildcards / Globbing)
通配符,也叫全局正则表达式(Globbing),是 Linux/Unix Shell 提供的一种极其强大的文件名匹配机制。它的核心作用是:让你不用敲全文件名,就能批量选中一组文件或目录。
当你输入带有通配符的命令(如 ls *.txt)时,Shell 会先在当前目录下找到所有符合条件的文件,把它们替换到命令行中,然后再把这个长长的列表交给命令去执行。
1. 常用的通配符符号
-
*(星号):匹配任意长度的任意字符 (新闻)
这是最常用、威力最大的通配符。它可以代表 0 个、1 个或多个任意字符。ls *.txt:列出所有以.txt结尾的文件(如a.txt,hello.txt)。ls data*:列出所有以data开头的文件(包含data本身,因为*可以代表 0 个字符)。ls *report*:列出文件名中包含report的所有文件。rm *:⚠️ 极其危险! 删除当前目录下的所有文件。
-
?(问号):严格匹配单个字符?只能代表刚好 1 个任意字符,不能是 0 个,也不能是多个。它通常用于文件名长度固定,但某个位置字符不确定的场景。ls file?.txt:会匹配file1.txt,fileA.txt。但不会匹配file10.txt(两个字符),也不会匹配file.txt(缺少该字符)。ls ???.log:列出所有名字刚好是 3 个字符的.log文件(如app.log,err.log)。
-
[ ](方括号):匹配指定范围内的单个字符
方括号用于定义一个“字符池”,它只匹配池子里的一个字符。ls file[123].txt:只匹配file1.txt,file2.txt,file3.txt。ls [0-9]*.txt:使用连字符-表示范围,匹配以任意数字开头的文件。ls file[!0-9].txt或ls file[^0-9].txt:使用!或^表示取反,匹配fileA.txt等,但剔除带有数字的。
-
{ }(花括号):扩展/排列组合 (Brace Expansion)
严格来说,{}不算传统意义上的“匹配型”通配符,它叫扩展符。*和?是去硬盘上找已存在的文件,而{}是让 Shell 凭空生成字符串的组合,常用于批量创建文件。touch {a,b,c}.txt:等价于执行touch a.txt b.txt c.txt。cp config.yaml{,.bak}:高阶运维技巧,等价于cp config.yaml config.yaml.bak(逗号前为空,代表原字符串)。mkdir -p 2024/{01..12}:利用双点号..表示序列,一键创建 12 个月份的子目录。
2. 通配符:新手最容易踩的 3 个大坑
- ❌ 坑 1:把“通配符”当成了“正则表达式”
在通配符中,*代表任意字符;但在正则中,代表任意字符的是.(点号),*只是一个数量词。 - ❌ 坑 2:通配符默认不匹配隐藏文件(Dotfiles)
在 Linux 中以.开头的文件是隐藏文件。如果你输入rm *,系统不会删除.bashrc。如果想专门操作隐藏文件,必须显式指定点号:ls .* - ❌ 坑 3:在
find命令中忘记给通配符加引号
如果执行find . -name *.txt且当前目录刚好有一个a.txt,Shell 会自作主张把*.txt替换成a.txt传给 find,导致搜不到其他子目录的文件。正确写法是find . -name "*.txt"。
二、 正则表达式 (Regular Expressions)
正则表达式是一种极其强大且复杂的文本模式匹配语言。就像医生用的手术刀,它用于精准剖析、查找、替换文件内部的文本数据(或者命令的输出结果)。
Linux 中的正则分为基础正则表达式(BRE)和扩展正则表达式(ERE,配合 grep -E 或 egrep 使用)。
1. 常用的正则表达式符号
-
.(点号):真正的“任意单个字符”
在正则中,点号才是通配符里?的等价物,它代表除换行符外的任意一个字符。grep "a.c" file:匹配abc,a1c,a-c等。
-
^和$:位置限定符(锚点)
它们不匹配具体的字符,而是匹配位置。grep "^Error" file.log:只匹配以Error开头的行。grep "done.$" file.log:只匹配以done.结尾的行。grep "^$" file.log:匹配空行(行首紧接着行尾)。
-
量词(限定符):控制出现次数
⚠️ 核心注意: 正则中的量词,修饰的是它前面紧挨着的那个字符或单元,而不是独立存在的!(新正文)*(匹配 0 次或多次):grep "ab*c"匹配ac,abc,abbbc。+(匹配 1 次或多次,ERE):grep -E "ab+c"匹配abc,abbbc,但不匹配ac。?(匹配 0 次或 1 次,ERE):grep -E "ab?c"匹配ac,abc,但不匹配abbc。{n,m}(匹配 n 到 m 次,ERE):grep -E "[0-9]{3,5}"匹配连续出现 3 到 5 次的数字。
-
[ ]、|和( ):字符集与逻辑结构[ ](字符集):grep "[a-zA-Z]"匹配任意大小写字母。[^0-9]匹配非数字。|(逻辑或,ERE):grep -E "cat|dog"匹配包含cat或dog的行。( )(分组,ERE):grep -E "(abc)+"将abc视为一个整体,匹配abc,abcabc等。
2. 正则表达式:新手最容易踩的 3 个大坑
- ❌ 坑 1:裸用
*号导致匹配失败
新手常想在文件中搜索以 “Error” 结尾的单词,于是写出grep "*Error" file。在正则中这是错的!*前面必须有字符给它修饰。
正确写法:grep ".*Error" file(.代表任意字符,*代表该任意字符出现无数次,这就等价于通配符里的*了)。 - ❌ 坑 2:忘记转义特殊字符
你想精确查找文件里的 IP 地址192.168.1.1,直接写grep "192.168.1.1"。结果它连192a168b1c1也匹配了,因为点号.在正则里是任意字符!
正确写法:使用反斜杠转义:grep "192\.168\.1\.1"。 - ❌ 坑 3:在基础命令中直接使用扩展正则符号
如果你写了grep "cat|dog" file,通常会一无所获,因为默认的grep只支持基础正则(BRE),它把|当成了普通字符。
正确写法:必须加上-E参数开启扩展正则支持:grep -E "cat|dog" file或使用egrep。
三、 最常见的混淆点对比(一张表看懂)
这是初学者最容易踩坑的地方,对照这张表,一目了然:
| 匹配意图 | 在“通配符”中应该写 (匹配文件名) | 在“正则表达式”中应该写 (匹配文件内容) | 举例说明 |
|---|---|---|---|
| 匹配任意一个字符 | ? |
. |
匹配 fileA.txt:通配符 file?.txt正则 file.\.txt |
| 匹配任意长度的任意字符 | * |
.* |
匹配 all.txt 等任意文件:通配符 *.txt正则 ^.*\.txt$ |
匹配点号 . 本身 |
. (直接写即可) |
\. (需要转义) |
匹配 a.txt:通配符 a.txt正则 a\.txt |
四、 黄金法则与进阶例外
你完全可以把 “通配符用于文件名字,而正则表达式用于文件内容” 作为你的第一反应和核心记忆点。
1. 日常 90% 的场景:完全符合上述法则
- 通配符 = Shell 帮你找文件。 敲下
rm *.txt时,Shell 关心的全是文件系统里有什么。 - 正则表达式 = 工具帮你找文本。 敲下
grep "error" file.log时,工具一行行阅读内容,只关心数据长什么样。
2. 进阶 10% 的场景:当界限被打破(小例外)
在 Linux 中,“一切皆文本”。所以这两者偶尔也会踩过界:
-
正则也能匹配文件名(把文件名当纯文本流处理)
当你用命令把文件名输出为文本流时,就可以用正则去筛了:ls | grep -E "^file[0-9]\.txt$"(ls吐出文本,grep用正则去接)。find . -regex ".*\.txt$"(find内置的正则引擎,直接用正则去搜索路径字符串)。
-
通配符也能匹配普通字符串(Shell 脚本内)
Bash 脚本本身支持用通配符去判断内存里的变量值,不一定要读写硬盘:[[ "hello.log" == *.log ]](Bash 特有的双中括号语法,用通配符判断字符串结尾)。
which 指令
which 的核心用途是:沿着 $PATH 查找某个外部命令对应的可执行文件路径,常用来确认当前环境优先找到的是哪个程序版本。它不能完整代表 Shell 对所有命令类型的解析结果。
本质
which 在很多 Linux 环境中是外部命令,常见位置如 /usr/bin/which,但具体实现也可能是脚本或 Shell 包装。它的核心工作通常是根据 $PATH 查找外部可执行程序,并不能完整复现 Shell 的全部命令解析过程。
完整执行流程
当你在终端输入 ls 时,Shell会按以下顺序解析:
- 先进行命令文本解析,其中包括别名(alias)展开等步骤;
- 真正查找命令时,Bash 会优先检查Shell 函数;
- 然后检查Shell 内置命令(如
cd、echo、export); - 最后再通过命令哈希/
$PATH查找外部可执行文件。which的核心能力主要集中在这一步。
可以把 which 的主要工作理解为围绕第 4 步进行:
- 解析
$PATH为以冒号:分隔的目录列表 - 从左到右依次遍历每个目录
- 找到第一个匹配的可执行文件立即返回
- 遍历完所有目录都没找到时通常返回非 0 状态;是否输出提示取决于具体
which实现。
注意:不同系统里的
which实现并不完全一样。 有的实现还会显示 alias 信息。因此判断 Bash 最终如何解析一个命令,优先使用type -a 命令或command -V 命令更可靠。
在开发和运维中,当我们安装了同一个软件的多个版本(比如 Python 2 和 Python 3,或者不同版本的 Node.js),which 是排查“环境变量冲突”和“到底在跑哪个版本”的头号利器。which 并不会在全盘盲目搜索(那样太慢了,那是 find 干的事)
1. 基本语法与工作原理
which [选项] 指令名称
[whb@bite-alicloud test]$ which ls
alias ls='ls --color=auto'
/usr/bin/ls
[whb@bite-alicloud test]$ which pwd
/usr/bin/pwd
注意: which 同一个命令第二次会变快?
当你第一次执行 which python 时,which 程序会老老实实地去读取你的 $PATH 环境变量(比如里面有 10 个目录),然后挨个去硬盘的这些目录里查找有没有叫 python 的可执行文件。
-
第一次(慢): 操作系统需要去读取 硬盘(Disk) 上的目录结构和文件信息。读取物理硬盘是非常耗时的操作。
-
系统的神助攻: Linux/macOS 等现代操作系统非常聪明,它看你刚查过这些目录,就会把这些目录的元数据(叫作 Dentry 和 Inode 缓存)顺手存到 内存(RAM) 里。
-
第二次(快): 当你再次执行 which python 时,which 依然是个“老实人”,它依然会从头把 $PATH 里的 10 个目录查一遍。但是!这一次操作系统拦截了请求,直接从内存中把目录信息甩给了它。从内存读数据比从硬盘读快几万倍,所以你感觉它“变快了”。
-
结论:变快的不是 which 记住了结果,而是操作系统缓存了路径信息。
2. 常见用法示例
示例 1:查看命令的真实路径
“我想知道我敲击 python 时,系统用的是系统自带的,还是我用 Anaconda 安装的?”
$ which python
/usr/bin/python # 这说明你使用的是系统默认路径下的 python
示例 2:检查某个命令是否安装
如果你想确认某个工具是否已经安装并且配置好了环境变量,直接用 which 测一下。
$ which docker
/usr/bin/docker # 有返回路径,说明已安装且在 PATH 中
$ which magic_tool
# 没有任何输出(或提示 not found),说明没安装或没加到环境变量里
3. 最核心的参数:-a (All)
默认情况下,which 找到第一个匹配的程序就会停止。但如果你想知道系统中到底有多少个同名的可执行文件在你的 $PATH 里,可以使用 -a。
场景:排查版本冲突
“我明明安装了新版 Node.js,为什么跑起来还是旧版?”
$ which -a node
/usr/local/bin/node # 系统先找到了这个(旧版)
/home/user/.nvm/versions/node/v18.0.0/bin/node # 这才是你想用的新版
排查结果:因为 /usr/local/bin 在 $PATH 中的顺序排在前面,所以系统优先用了旧版。你需要调整环境变量的顺序,或者删掉旧版。
4. 新手最常见的“坑”与局限性
❌ 坑 1:找不着“Shell 内置命令”
which 主要针对 $PATH 中的外部可执行文件。对于 Shell 内置命令、函数和别名,不同实现的显示行为可能不同,所以不能把 which 当成判断命令类型的最终依据。
$ which cd
# 通常没有输出,或者提示 no cd in (...)
原因:cd、history 等属于 Shell 内置功能;echo、pwd 等虽然常有 Shell 内置版本,但系统中也可能同时存在 /usr/bin/echo、/usr/bin/pwd 这样的外部版本。
❌ 坑 2:找不着“别名 (alias)”
如果你在 .bashrc 中设置了 alias ll='ls -l',某些 which 实现会显示这个 alias,另一些只按 $PATH 查找而没有结果。因此排查 alias 时,直接使用 type ll 或 alias ll 更明确。
5. 进阶拓展:更好的替代品
既然 which 有局限性,老鸟们通常会结合以下两个命令来更全面地摸清底细:
-
type(强烈推荐👍):
它是 Shell 的内置命令,比which更聪明。它不仅能找到文件路径,还能明确告诉你这个命令是别名、内置命令还是外部文件。$ type cd cd is a shell builtin # 告诉你这是内置命令 $ type ll ll is aliased to `ls -alF' # 告诉你这是别名 $ type python python is /usr/bin/python # 告诉你这是外部程序 -
whereis:
比which找得更宽泛。它不仅找可执行文件的路径,还会顺带帮你把该命令的源代码和man 帮助文档的路径一起揪出来。$ whereis nginx nginx: /usr/sbin/nginx /etc/nginx /usr/share/man/man8/nginx.8.gz
补充:命令其实是shell外壳?
Shell 命令是面向 Shell 的,内核并不认识 ls、cd、find、grep 这些“命令语法”。
这个表述只对了一半。
如果把这句话修正为最严谨的技术定论,应该是:Shell 是包裹在操作系统内核(Kernel)外面的“壳”;而命令分为两种,有一部分命令确实是 Shell 自身的一部分,但绝大多数命令是独立于 Shell 之外的独立程序。
1. 架构宏观视角:Shell 到底是个什么“壳”?
在 Linux 的世界里,层次是非常分明的(洋葱模型):
- 最内层(硬件 Hardware):CPU、内存、硬盘。
- 核心层(内核 Kernel):真正拥有至高无上权力、能直接操控硬件的代码。但它非常傲娇,不接受普通用户的直接指令。
- 外壳层(Shell):这就是所说的“外壳”。它是一个命令行解释器(Command Line Interpreter)。它的核心工作是解析用户输入的命令、变量、通配符、重定向、管道等语法:如果是内置命令就在 Shell 内部处理,如果是外部命令就查找并启动对应程序。Shell 自己或被启动的程序在需要时再通过系统调用请求内核服务,并不是把每一条命令简单“翻译成一个系统调用”。
- 最外层(用户 User):也就是坐在键盘前的你。
所以,Shell 包裹的是内核,而不是命令。
2. 核心揭秘:命令的两大阵营
当你敲下一个命令时,Shell 这个翻译官会将其分为两大类来处理。这就是为什么说"对了一半”的原因:
阵营 A:内部命令 (Shell Built-in Commands) —— “理解是对的”
- 本质: 这些命令的实现内置在 Shell 程序(比如 Bash)内部,执行内置版本时不需要通过
$PATH找独立文件。不过某些同名命令(如echo、pwd、printf)在系统中也可能同时存在外部程序版本。 - 执行方式: 当你敲下这些命令时,Shell 不需要去硬盘上找任何东西,直接在自己的内存进程里运行一段逻辑就结束了。速度极快。
- 经典代表:
cd(切换目录,它必须是内部命令,因为外部程序无法改变当前 Shell 的工作路径)pwd(打印当前路径)echo(输出字符串)umask(掩码,它改变的是当前 Shell 的环境,所以必须是内部的)
对于这部分命令,结论完全正确:它们确实就是 Shell 外壳的一部分。
阵营 B:外部命令 (External Commands) —— “理解是错的”
- 本质: 它们是独立存在于硬盘上的二进制可执行文件。它们和你自己用 C++ 编译出来的
./a.out在本质上没有任何区别,仅仅是存放的位置不同而已。 - 执行方式: 当你敲下外部命令时,Shell 会去我们之前讲过的
$PATH(寻宝图)里翻找。找到文件后,Shell 会向内核申请创建一个新的子进程(Fork & Exec) 来运行这个程序。 - 经典代表:
ls(通常位于/bin/ls)cat(通常位于/bin/cat)chmod(通常位于/bin/chmod)file(通常位于/usr/bin/file)
对于这部分命令,它们根本不是 Shell,Shell 只是负责把它们从硬盘上“揪”出来运行的中介。
3. 实战“照妖镜”:如何区分内部还是外部?
在 Linux 中,有一个专门用来鉴定命令真身的指令:type。你可以亲自在终端里做个实验:
测试内部命令:
$ type cd
cd is a shell builtin
翻译:cd 是一个 shell 内建命令 (证实了它就是 Shell 本身的一部分)
测试外部命令:
$ type ls
ls is aliased to `ls --color=auto' (有的系统会先显示别名)
$ type -a ls
ls is /usr/bin/ls
翻译:ls 是存放在 /usr/bin/ls 里的一个独立文件 (证实了它独立于 Shell)
补充:什么是bash
简单用一句话概括:Bash 就是你和 Linux 系统沟通的“翻译官”兼“大管家”。 (bash就是一种shell)
为了让你彻底懂它的定位,我们把概念拆解一下:
1. 为什么它被称为 Shell(壳)?
在计算机的架构里,有一个经典的分层概念:
- Kernel(内核):这是操作系统的“心脏”,负责直接控制底层的硬件(CPU、内存、硬盘)。但内核非常高冷且复杂,只懂机器码,人类根本没法直接和它说话。
- Shell(外壳):为了保护内核,同时也为了让人类能轻松下达指令,操作系统在内核外面包了一层“壳”。
Bash 就是目前世界上最流行、也是绝大多数 Linux 系统(如 Ubuntu, CentOS)默认采用的一种 Shell。
当你敲下 ls、g++ main.cpp 或者前面学的 cat << EOF 并回车时,并不是内核直接理解这串 Shell 文本。Bash 会先解析其中的命令、变量、重定向、通配符等语法,然后执行内置命令或启动外部程序;Shell 和外部程序在运行过程中再通过系统调用请求内核服务,最后把输出交给终端显示。
2. Bash 的两重身份
Bash 不仅仅是一个让你敲黑底白字命令的窗口,它其实身兼两职:
- 身份一:交互式命令行解释器
这就是你平时的操作模式:你敲一行命令,它立刻给你一个反馈。你之前学的重定向 (>)、管道 (|)、通配符 (*),全都是 Bash 这套软件自带的语法特性。 - 身份二:一门强大的胶水编程语言
你可以把几十个、上百个零散的 Linux 命令,配合上if(条件判断)、for(循环)、变量等逻辑,写进一个文件里(通常以.sh结尾)。这就是大名鼎鼎的 Bash 脚本 (Shell Script)。后端开发和运维工程师就是靠写 Bash 脚本,把繁琐的操作变成“一键全自动部署”的。
3. 一个极客冷知识:为什么叫 “Bash”?
Bash 的全称是 Bourne Again SHell。
早年间,Unix 系统上最早且最火的 Shell 是由一位叫 Stephen Bourne 的大神写的,程序名叫 sh。后来,开源的 GNU 计划决定写一个功能更强大、且完全免费的替代品。为了致敬前辈,同时程序员们玩了一个英文的宗教谐音梗(Born again,意为“重生”),就把它幽默地命名为了 “Bourne Again SHell”(重生的 Bourne Shell),简称 Bash。
补充:Linux 指令全都是“可执行文件”吗?
绝不全都是! 很多初学者觉得:“命令嘛,肯定就是存放在电脑某个目录(比如 /usr/bin)下的一个小程序,我敲命令,系统就去运行它。”
这是一个巨大的误区。实际上,只有“外部命令”才是可执行文件!
在 Linux 中,你在终端敲下的“命令”,其实分为 4 大门派。为了方便理解,我们把 Bash(也就是负责执行命令的 Shell 程序)想象成一位“主厨”,来看看这 4 大门派到底是什么:
门派 1:别名 (Alias) —— 大厨的“快捷短语”
- 是什么:你给长命令起的外号。比如你嫌
ls -l太长,搞了个alias ll='ls -l'。 - 记录在哪:只存在大厨的脑子里 (Bash 进程的内存中)或者写在
~/.bashrc配置文件里。 - 是不是文件? 绝对不是。
$PATH记录了吗? 没有!which能找到吗? 原生的which找不到(它只找实体文件)。
门派 2:Shell 内置命令 (Built-in) —— 大厨的“天生本能”
- 是什么:像
cd(走路切换位置)、pwd(看看现在站哪)、exit(下班)。这些是 Bash 源代码里自带的函数,一旦你打开终端,它们就已经在内存里准备好了。而且,像cd这种改变当前环境的动作,必须由大厨亲自来做,外部工具做不到。 - 记录在哪:存在大厨的脑子里(Bash 进程的内存中)。
- 是不是文件? 绝对不是实体文件!
$PATH记录了吗? 没有!which能找到吗? 找不到!(这就是为什么which cd会报错找不到,而系统依然能执行cd)。
门派 3:Shell 函数 (Function) —— 大厨的“自创菜谱”
- 是什么:用户自己在终端里临时写的一段带有逻辑(if/for)的代码块。
- 记录在哪:存在大厨的脑子里(Bash 进程的内存中)。
- 是不是文件? 不是。
$PATH有记录吗? 没有!which能找到吗? 找不到!
门派 4:外部可执行文件 (External) —— 躺在仓库里的“实体工具”
- 是什么:这才是实实在在躺在硬盘上的程序文件!比如
ls.exe、python、find、cat。它们可能是 C 语言编译好的二进制文件,也可能是脚本。 - 记录在哪:存放在硬盘的各个“仓库”目录下(如
/usr/bin、/sbin、/usr/local/bin)。 - 是不是文件? 是!只有这一类才是真正的可执行文件。
$PATH有记录吗? 有!只有这一类归$PATH管。$PATH记录的不是ls这个词,而是记录了/usr/bin等等这些仓库的地址。which能找到吗? 能!这是which唯一能找到的东西。
💡 终极破案:为什么 which 找 $PATH 却找不到某些命令?
现在逻辑完全闭环了。很多人踩过这个坑:明明 cd 命令能用,为什么 which cd 却报错?
- 当你在终端输入
cd(内置命令)时,大厨直接动用脑子里的技能就执行了,根本没去看$PATH这张地图,也没去仓库找工具。 - 此时,你偏偏派
which这个“仓库管理员”去查cd在哪。 which是一个极其死板的管理员。它严格拿着$PATH这张地图,跑去/usr/bin等实体仓库里翻箱倒柜。- 仓库里当然没有
cd这个实体工具(因为它是大厨的脑力技能)。 - 所以,
which顺着$PATH找了一圈,无奈地告诉你:“找不到”。
一句话总结:$PATH 并不是全天下所有命令的“生死簿”,它只是一张“外部实体工具存放指南”。那些存在于内存里的别名、内置命令和函数,根本不在 $PATH 的管辖范围内,死死依赖 $PATH 寻址的 which 自然也就对它们视而不见了。
🔍 终极大招:一招辨别指令的“真身”
以后无论遇到什么指令,不要去猜它是哪个门派的。Linux 给我们提供了一个比 which 更强大的“终极照妖镜”命令 —— type -a,它会立刻告诉你这个指令到底是个什么东西:
- 测别名:
$ type -a ll
ll is aliased to `ls -l' # 明确告诉你这是一个别名
- 测内置命令:
$ type -a cd
cd is a shell builtin # 明确告诉你这是一个内置命令
- 测外部命令(可执行文件):
$ type -a find
find is /usr/bin/find # 丢给你一个硬盘路径,说明它是可执行文件
日常开发体会: 你平时感觉“大部分”指令都是可执行文件,是因为我们要处理业务干活时(处理文件、搜索、跑程序),用的基本都是外部命令;但当你控制系统环境和终端本身时,用的往往是内置命令。
补充:自己写的程序为什么要有./才能跑?
这是一个几乎所有从 Windows 转向 Linux/C++ 开发的初学者都会产生的灵魂拷问:为什么系统自带的 ls、pwd 直接敲名字就能运行,而我自己辛辛苦苦编译出来的程序(比如 a.out),非得在前面加个别扭的 ./ 才能跑?
这绝不是 Linux 设计师在故意刁难你,而是基于一个极其硬核的底层寻址逻辑和系统安全防御机制。
为了把这个知识点写透,我们可以将它拆解为以下三个核心板块:
1. 系统的“四步寻址法”与寻宝图 $PATH
当你敲下一个命令(比如 ls)按下回车时,Bash(系统的大管家)并不会全盘扫描硬盘,它也不会一上来就直接去看 $PATH。系统有着一套极其严谨的寻找逻辑:
- 第一步:展开“别名 (Alias)”。Bash 在命令读取/解析阶段会先处理 alias 展开。例如有些系统会设置
alias ls='ls --color=auto'。展开后,命令仍会继续按正常规则解析执行,并不是简单“直接跳到第四步”。 - 第二步:查“函数 (Function)”。Bash 会先检查当前 Shell 是否定义了同名函数。
- 第三步:查“内置命令 (Built-in)”。再检查它是不是 Bash 自带的内置命令(比如
cd、pwd)。 - 第四步:按命令哈希 /
$PATH寻宝图找“外部实体文件”。前面的函数和内置命令都没有匹配时,Bash 才会通过命令哈希以及 环境变量$PATH去文件系统中寻找外部可执行程序。
你可以自己查看一下这张藏宝图:
echo $PATH
你会看到一串由冒号 : 隔开的目录路径,大概长这样:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
在第四步中,系统翻硬盘的具体逻辑是这样的:
- 系统会立刻去
$PATH里的第一个目录(/usr/local/sbin)找,有没有叫ls的可执行文件? - 如果没有,就去第二个目录找,以此类推。
- 如果在
/bin或/usr/bin目录里找到了,就立刻执行它! - 如果找遍了
$PATH里的所有目录都没找到,系统就会无奈报错:command not found。
为什么你的代码不能直接运行?
因为你编译出来的程序(比如 a.out 或 my_script.sh),通常存放在你当前的工作目录(比如 ~/project/)下。而你当前的目录,并不在 $PATH 这张寻宝图里。 既然前三步的内存里没有,第四步的寻宝图里也没有,系统当然就报告找不到了。
2. 破译 ./ 的真实含义:精准的物理定位
既然系统按图索骥找不到,你就必须给系统一个绝对明确的位置指引。这就引出了 ./ 的真实含义。
在 Linux 的文件系统中:
/代表根目录(或者作为目录分隔符)。..代表上一级目录。.代表当前所在的目录。
当你输入 ./a.out 时,你其实是在对系统下达一个极其精确的指令:
“不要走那四步寻找流程了,也不要去 $PATH 里的那些系统目录找了,就给我直接执行当前目录(.)下的这个(/)名叫 a.out 的实体程序!”
这就是加上 ./ 的本质:将“按名盲搜”直接切换成了“按物理路径精准执行”。
3. 终极拷问:为什么 Linux 不把当前目录(.)默认加入 PATH?
很多初学者会想:既然每次都要加 ./ 这么麻烦,为什么 Linux 系统的开发者不聪明一点,直接把 .(当前目录)默认加到 $PATH 的最前面呢?Windows 系统就是这么干的呀(Windows 在执行命令时,默认会先搜索当前目录)。
因为在多用户共享的 Linux 环境下,这会引发一场灾难级的安全漏洞——“木马替换攻击”。
我们在 /tmp 共享目录,来推演一下这个黑客攻击场景:
- 假设:Linux 允许默认搜索当前目录。
- 埋雷:黑客在一个公共目录(比如
/tmp)下,偷偷写了一个恶意的破坏脚本,并且把它命名为ls。 - 踩雷:系统管理员(root)为了排查问题,进入了
/tmp目录。他习惯性地敲了一下ls想看看目录里有什么。 - 爆炸:因为系统优先搜索当前目录,它找到了黑客伪造的
ls并以 root 权限执行了它,而不是执行真正的/bin/ls。黑客瞬间拿到了服务器的最高控制权!
Linux 的安全哲学:
为了彻底杜绝这种“同名伪装”攻击,Linux 采取了“零信任”原则。它只信任 $PATH 里那些由管理员严格管控的系统目录(如 /bin, /usr/bin)。任何不在信任名单里的程序,哪怕它就在你眼皮子底下,你也必须使用绝对路径(如 /home/user/code/a.out)或相对路径(./a.out)来强行指定运行。
💡 进阶实战:如何优雅地摆脱 ./?
如果你写了一个非常牛的 C++ 工具,希望像系统命令一样,在任何目录下直接敲名字就能运行,你有两种符合规范的专业做法:
方法一:将程序移动到系统的默认寻宝区(推荐)
把你的编译好的程序,复制或移动到 /usr/local/bin(对所有用户生效)或 ~/.local/bin(仅对自己生效)目录下。
sudo cp a.out /usr/local/bin/my_tool
# 以后在任何地方,直接输入 my_tool 就能运行了
方法二:把你的代码目录,画到寻宝图里(适合本地开发)
修改你的 ~/.bashrc 文件,将你专门存放代码的目录追加到 $PATH 中:
export PATH=$PATH:/home/mounanlin/my_cpp_projects/bin
(注意:追加的绝对路径请务必放在 $PATH 变量的后面 PATH=$PATH:你的路径,这样即使你有同名文件,系统也会先执行系统自带的安全程序,防止你自己坑自己。)
whereis指令
功能: 是 Linux/Unix 系统中用于快速定位二进制可执行文件、源代码、man 手册页路径 的命令,只搜索标准系统目录(非自定义 PATH / 用户目录),比 find 快、比 which 范围更广。
你可以把 whereis 想象成一个 “档案管理员”。它不仅能帮你找到可执行文件在哪,还会“买一送二”,顺便把这个命令的 源代码文件(Source) 和 帮助文档(Man pages) 一起给你翻出来。
1. 核心功能与基本语法
whereis [选项] 指令名称
默认情况下,当你输入 whereis 命令 时,它会尝试返回三个东西的路径:
- Binary(二进制可执行文件)
- Source(源代码文件,如果有的话)
- Manual(Man 帮助手册的路径)
示例:
$ whereis nginx
nginx: /usr/sbin/nginx /etc/nginx /usr/share/man/man8/nginx.8.gz
解读:输出结果以 nginx: 开头,后面跟着它找到的所有相关路径。第一个是执行文件,第二个通常是配置目录(有些系统配置下会被扫出来),第三个是压缩好的帮助文档。
2. 常用选项(精确打击)
如果你觉得默认输出的东西太多了,只想找特定的文件,可以通过选项来限制:
-b(Binary):只找可执行文件。$ whereis -b python python: /usr/bin/python /usr/bin/python3.8-m(Manual):只找帮助文档。$ whereis -m ls ls: /usr/share/man/man1/ls.1.gz-s(Source):只找源代码。(绝大多数通过包管理器安装的普通软件通常没有带源码,所以这项经常为空)。$ whereis -s ls ls:
3. whereis 与 which 的巅峰对决(核心区别)
这是新手最容易搞混的地方。既然都能找命令路径,它俩到底有什么区别?
区别 A:找东西的“地图”不一样
which的地图是$PATH:你配置了什么,它就顺着找什么。只要在$PATH里,你放在桌面上的脚本它也能找到。whereis的地图是“硬编码的系统标准目录”:whereis根本不看你的$PATH!它有一张自己内部写死的搜查清单(通常包含了 Linux 标准的/bin,/usr/bin,/sbin,/usr/share/man等目录)。- 影响:如果你自己写了个脚本放在
~/.local/bin里,which能找到,但whereis大概率找不到,因为它嫌弃这个非标准目录,压根没去搜。
- 影响:如果你自己写了个脚本放在
区别 B:工作的目的不一样
which解决的是“执行冲突”:它告诉你,“当我敲下回车时,系统确切地在运行哪一个文件”。它找到第一个就立刻停止。whereis解决的是“资产盘点”:它告诉你,“关于这个软件,系统标准目录里到底散落了多少相关文件”。它会把找到的所有同名执行文件、文档全列出来。
4. 常见应用场景
场景 1:想看某个命令的帮助文档,但又不想直接进入阅读模式
“我想知道 tar 命令的文档到底存放在硬盘的哪里,方便我拷给别人。”
$ whereis -m tar
场景 2:排查系统里安装了几个版本的软件
因为 whereis 不会像 which 那样找到第一个就停下,它会把标准目录下所有的相关文件都揪出来:
$ whereis node
node: /usr/bin/node /usr/local/bin/node /usr/share/man/man1/node.1.gz
这说明系统标准目录下有两个 Node.js 的可执行文件。
5. 易错点与局限性
- 找不到别名 (Alias) 和内置命令 (Built-in):和
which一样,whereis也是个外部程序,它只能翻找硬盘上的实体文件,无法穿透 Shell 的内存去查看别名和内置技能(比如whereis cd往往只能找到那个备用的可执行文件,甚至什么也找不到)。 - 不适合找“非标准安装”的软件:如果你把软件安装在了
/opt/my_custom_app/这种非主流目录下,除非你手动用-B参数强行给whereis指定搜索路径,否则它默认是绝对找不到的。
总结:
如果你想知道“我刚刚敲的命令究竟在跑哪里的代码”,用 which(或更好的 type -a);如果你想知道“这个软件的周边文件(手册、备用可执行文件)都在系统标准目录的哪里”,用 whereis。
alias(别名)指令。
我们在前面的讨论中已经给它定了性:alias 属于 Shell 的内置命令,它本质上就是你给长串命令起的 “外号” 或者 “快捷键” 。它不存在于硬盘上,而是直接写在 Bash 大厨的脑子里。
注意:
1.执行命令时,系统会优先搜索别名(alias),如果不存在,再去 /usr/bin/ 等目录下寻找外部可执行文件
2.完整的 Linux 命令真实查找顺序(优先级从高到低):
第一顺位:别名 (Alias) —— 最高特权
第二顺位:内部命令 (Built-in) —— 大厨本能
第三顺位:Hash 缓存 —— 历史记忆
第四顺位:环境变量 $PATH —— 翻箱倒柜(图片后半句的意思)
1. 基本语法与核心用法
🔹 查看当前系统的所有别名
直接敲击 alias,不加任何参数:
$ alias
alias cp='cp -i'
alias ll='ls -l --color=auto'
alias rm='rm -i'
你会发现,即使你什么都没设置,系统也已经偷偷帮你贴心配置了几个(比如防误删的 rm -i)。
🔹 创建一个新别名
语法极其严格:alias 新名字='原本的长命令'
⚠️ 致命易错点:等号 = 的两边绝对不能有任何空格!
- ❌ 错误:
alias ll = 'ls -l'(会报错) - ✅ 正确:
alias ll='ls -l'
2. 实战高频场景(老鸟的日常)
为什么要起别名?主要是为了防呆、防手残、省手指。
场景 1:给高危操作加上“后悔药”
Linux 的删除是直接抹除的。我们可以强制让 rm 每次执行前都问一句“你确定吗?”(-i 参数):
alias rm='rm -i'
alias mv='mv -i'
场景 2:缩写极其冗长的高频命令
比如经常需要连服务器、或者敲很长的 Git 状态命令:
alias gs='git status'
alias gpush='git push origin master'
alias server1='ssh root@192.168.1.100 -p 2222'
以后你想连服务器,只需敲 server1 回车即可,简直不要太爽。
场景 3:拯救你的“手残”拼写
如果你的手指经常打架,把 ls 打成 sl,或者把 clear 打成 c,别名能帮你兜底:
alias sl='ls'
alias c='clear'
3. 最大号的坑:“关机就没”与如何永久保存
这是所有新手都会踩的坑:你在终端里敲 alias mycmd='...' 建立的别名,是临时写在当前终端内存里的。只要你把窗口一关,或者重启电脑,它就灰飞烟灭了。
如何让它永久生效?
你需要把它写进大厨的“祖传菜谱”里。对于绝大多数 Linux 系统,这个菜谱文件是用户家目录下的 .bashrc(如果你用 Zsh,就是 .zshrc)。
操作步骤:
- 用编辑器打开配置文件:
vim ~/.bashrc - 滑到文件最末尾,另起一行,写下你的别名:
alias up='sudo apt update && sudo apt upgrade -y' - 保存并退出。
- 唤醒菜谱(让配置立刻生效,无需重启):
source ~/.bashrc
4. 进阶玩法:如何“卸掉伪装”和“解除别名”
🔹 临时绕过别名(叫它的真名)
假设系统设置了 alias ls='ls --color=auto',但你今天偏偏就是不想看带颜色的输出,你想运行最原始、毫无修饰的那个硬盘里的可执行文件。
你有两种方法可以临时越过大厨的别名,直接调用外部程序:
- 在命令前加反斜杠
\(最常用):\ls - 写绝对路径(用绝对的物理地址压制它):
/usr/bin/ls
🔹 删除别名 (unalias)
注意:unalias 只是短暂生效,它绝对不会修改你的配置文件!
如果你觉得某个别名很烦,或者它跟你想运行的脚本重名了,可以剥夺它的称号:
$ unalias ll
# 此时再输入 ll 就会提示 command not found 了。
为什么只是短暂生效?
- 配置文件(如
~/.bashrc) 就像是大厨的“祖传菜谱”,白纸黑字写在硬盘里的。 - 当前终端(Bash 进程) 就像是大厨“今天的大脑记忆”。大厨每天早上上班(你打开一个新终端),第一件事就是把“祖传菜谱”背一遍,记在脑子里。
当你执行 unalias ll 时,你其实是对大厨说:“你今天把 ll 这个快捷短语给我忘掉!”
大厨听话地从脑子(当前内存)里抹掉了这个别名。所以在你关闭这个终端之前,ll 都失效了。
但是! 你的指令根本没有权限,也没有能力去涂抹那本“祖传菜谱”。
所以,只要你关闭当前窗口,重新打开一个新终端(大厨明天又来上班了),大厨再次翻开 ~/.bashrc,ll 这个别名又会满血复活!
如何“永久删除”一个别名? 如果你下定决心,这辈子都不想再看到这个别名了,你需要直接去修改“菜谱”。
第一步:揪出菜谱里的记录
用文本编辑器(比如 nano 或 vim)打开你的 Bash 配置文件:
nano ~/.bashrc
第二步:物理删除
在文件里找到类似 alias ll='ls -l' 的这一行。你可以直接把它删掉,或者在它前面加一个 # 号把它注释掉(推荐注释,万一以后想加回来呢):
# alias ll='ls -l' <-- 就像这样加上 # 号
然后保存并退出。
第三步:让大厨重新背诵菜谱
此时即使你改了文件,当前终端的大厨依然按老记忆在干活。你需要让他立刻把新菜谱重新读一遍。执行这个极其高频的命令:
source ~/.bashrc
(source 的意思就是:不要等下次开机了,现在立刻马上把这个配置文件的内容在当前内存里重新执行一遍!)
做到这一步,这个别名才算是被彻底、永久地从你的系统中抹除了。
补充: Linux 命令行的“流程控制符”。
在实际的开发和运维中,我们经常需要让 Bash 帮我们自动按顺序执行一连串的命令,而不是敲一个等一会儿再敲下一个。
这些符号本质上是你在向 Bash(大管家)下达“连环任务”时使用的连接词。掌握它们,你能写出极其优雅且健壮的单行命令。
1. && :逻辑与 (AND) —— “成功了,才继续”
- 动作原理:Bash 会先执行左边的命令。只有当左边的命令成功执行(退出状态码为 0)时,才会接着执行右边的命令。如果左边失败了,右边直接被跳过,整个任务终止。
- 实战场景:前后有强依赖关系的操作。最经典的就是 C/C++ 的源码编译安装。
# 只有 configure 成功,才执行 make;只有 make 成功,才执行 make install
./configure && make && sudo make install
# 进入目录,如果进入成功,才执行删除(防止目录不存在导致误删当前目录文件)
cd /tmp/cache && rm -rf *
2. || :逻辑或 (OR) —— “失败了,才兜底”
- 动作原理:与
&&恰恰相反!Bash 先执行左边。只有当左边执行失败(退出状态码非 0)时,才会去执行右边的命令。如果左边成功了,右边直接被跳过。 - 实战场景:错误处理、兜底逻辑或者环境检查。
# 尝试进入 build 目录,如果目录不存在(cd 失败),就立刻创建一个
cd build || mkdir build
# 检查某个进程是否在运行,如果不在(grep 失败),就重启它
pgrep nginx || systemctl start nginx
3. ; :分号 —— “不管死活,按顺序排队”
- 动作原理:它是最无脑的连接符,等同于键盘上的“回车键”。Bash 会先执行左边,等左边执行完(不管成功还是报错),立刻接着执行右边。彼此之间没有任何逻辑依赖。
- 实战场景:单纯为了省事,把多行命令合并成一行执行。
# 依次执行,哪怕 date 报错了,依然会继续执行 ls 和 pwd
date ; ls -l ; pwd
4. | :管道符 (Pipe) —— “流水线接力”
🚨 注意:虽然长得像,但它和上面三个有本质区别!上面三个控制的是执行顺序,而管道符控制的是数据流。
- 动作原理:把左边命令的标准输出(正常产生的数据),直接作为右边命令的标准输入(水源)。
- 实战场景:文本过滤、数据加工。
# cat 读取文件所有的内容,倒进管道;grep 从管道里接水,只把带有 "ERROR" 的那几滴水过滤出来
cat /var/log/syslog | grep "ERROR"
5. & :单后台符 (Background) —— “挂在后台,立刻交权”
- 动作原理:把它放在命令的最后面,告诉 Bash:“这个任务跑得很慢,你把它丢到后台去偷偷执行,立刻把终端输入框还给我,我还要干别的。”
- 实战场景:运行耗时极长的数据处理脚本,或者启动服务器程序。
启动你的 C++ 服务,不霸占当前终端
./my_cpp_server &
💡 高阶组合技:Bash 的“三元表达式”
在实际的 Bash 脚本或单行命令中,经常会把 && 和 || 连起来用,看起来有点像 C++ 里的 condition ? true_action : false_action,但它并不是真正等价的三元运算符。
经典组合语法:命令 A && 命令 B || 命令 C
- 逻辑翻译:先执行 A;A 成功才执行 B。随后只要前面的
A && B整体结果为失败,就会执行 C。因此如果 A 成功但 B 自己执行失败,C 也会执行。 只有在你明确知道 B 会成功返回 0 时,才适合把它简单类比成“三元表达式”。
实战演示:
# 检查某个文件是否存在。存在打印 OK,不存在打印 报警!
cat config.json > /dev/null 2>&1 && echo "文件存在,OK" || echo "警报:文件丢失!"
补充:Linux 的注释符号
在 Linux 的 Bash / sh 这类 Shell 脚本语法中,标准的单行注释符号是 #。不同系统配置文件有各自的格式,不能说绝大多数 Linux 配置文件只有这一种“唯一标准注释符号”。
// 不是 Bash / sh 的注释语法!
如果你有 C++ 或 Java 的编程背景,很容易习惯性地顺手敲一个 //。但在 Linux 命令行或脚本里,系统会直接报错,因为它会把 / 严格当成目录路径的分隔符。
为了让你在 Linux 下写脚本不出错,这里为你详细梳理一下 Linux 注释的 3 个核心知识点:
1. 标准单行注释:#
只要在一行中出现了 #,从 # 开始直到这一行结束的所有内容,都会被 Bash(系统大管家)当作废话,直接无视。
# 这是一整行的注释,用来解释接下来的操作
rm -rf /tmp/cache/*; ls -lah # 这是行尾注释;先执行 rm,再执行 ls,# 后面的文字被忽略
2. 🚨 一个极其重要的特例:Shebang (#!)
如果你打开任何一个专业的 Linux 脚本(比如 .sh 文件),第一行几乎永远是这样一个以 # 开头的神秘符号:
#!/bin/bash
echo "Hello World"
注意:当脚本文件被直接执行时,文件开头的 #! 具有特殊意义。 对 Shell 自己来说这一行外观上仍像注释,但当它位于文件开头时,操作系统会根据它指定的解释器来启动脚本。
这个特殊的组合 #! 在黑客圈被称为 Shebang(发音:释伴)。它的作用是给系统指路:当系统执行这个文件时,第一眼看到 #!,就会立刻明白:“哦,我不能随便瞎执行,我得去 /bin/bash 这个路径下找 Bash 这个翻译官,来解析下面的代码。”
(注:只有出现在文件的绝对第一行,#! 才有这个法力;出现在其他地方,它就只是个普通的注释。)
3. 高阶技巧:多行注释怎么搞?
在 C++ 里,我们可以用 /* ... */ 痛快地注释掉一大堆代码。但 Bash 语法里根本没有原生的多行注释符号。
如果是正常操作,你只能极其苦逼地在每一行前面敲一个 #。
但聪明的极客们发明了一个黑魔法(正好用到了我们之前学的 << 嵌入文档语法):
Linux 中有一个极其特殊的命令叫作 :(冒号),它的意思是“什么都不做,直接返回成功”。配合 <<,你就能弄出一个完美的伪多行注释:
: << 'EOF'
这中间的代码由于有 bug,我先注释掉。
无论这里写了多少行,敲了多少个回车,
系统都会把它们打包喂给 ":" 这个不干活的命令。
从而完美实现了多行注释的效果!
EOF
一句话总结:
在写 Shell 脚本时,记住使用 # 作为注释符号;但在 Vim 中编辑 C/C++、Java 等源代码时,当然仍然遵循对应编程语言自己的注释规则,例如 C++ 可以使用 //。
在vim写代码时候可以用//
grep 指令
如果说 find 是负责找文件的“地毯式搜查员”,which 是专门查程序的“户籍警”,那么今天我们要讲的 grep,就是 Linux 系统里的 “超级文本显微镜”。
grep(Global Regular Expression Print)的核心功能极其专一且强大:它不关心文件名,它专门负责钻进文件的肚子里,或者截获其他命令的输出,把你想要的“文本内容”一行一行地揪出来。
1. 基本语法与工作原理
grep [选项] "匹配模式" [目标文件...]
工作原理:grep 会一行一行地读取目标文件(或传给它的文本流)。如果某一行包含了你指定的“匹配模式”,它就会把这整行打印到屏幕上;如果不包含,就默默跳过。
最基础用法:
在 config.txt 文件中寻找包含 "admin" 的行
grep "admin" config.txt
文件内容:
[whb@bite-alicloud test]$ cat temp.txt
abcd
ABCD
hello
bite
1234
基本查找
[whb@bite-alicloud test]$ grep "abcd" temp.txt
abcd
忽略⼤⼩写的不同,所以⼤⼩写视为相同
[whb@bite-alicloud test]$ grep -i "abcd" temp.txt
abcd
ABCD
顺便输出⾏号
[whb@bite-alicloud test]$ grep -n "abcd" temp.txt
1:abcd
[whb@bite-alicloud test]$ grep -ni "abcd" temp.txt
1:abcd
2:ABCD
反向选择,亦即显⽰出没有 '搜寻字符串' 内容的那⼀⾏
[whb@bite-alicloud test]$ grep -v "abcd" temp.txt
ABCD
hello
bite
1234
[whb@bite-alicloud test]$ grep -vn "abcd" temp.txt
2:ABCD
3:hello
4:bite
5:1234
[whb@bite-alicloud test]$ grep -vni "abcd" temp.txt
3:hello
4:bite
5:1234
2. 运维老鸟最高频使用的 5 个参数
grep 的参数极多,但日常工作掌握这 5 个就能解决 99% 的问题:
-
-i(Ignore case):忽略大小写
“我不确定日志里写的是 Error、ERROR 还是 error。”grep -i "error" app.log -
-v(Invert match):反向匹配(剔除,亦即显⽰出没有’搜寻字符串’内容的那⼀⾏)
极其重要!找出不包含该模式的行。
“查看配置文件,但把带#号的注释行全部过滤掉。”grep -v "^#" /etc/ssh/sshd_config -
-n(Line number):显示行号
“找到了错词,但我想知道它在文件里的第几行,方便我去改。”grep -n "failed" /var/log/syslog -
-r/-R(Recursive):递归搜索目录
这是grep跨界抢find生意的神技。不用配合find,直接让grep钻进一个目录里的所有文件中去找内容。
“我忘了那个数据库密码写在哪个代码文件里了,去/var/www/目录下全部翻一遍!”grep -r "db_password" /var/www/
3. grep 的真正灵魂:结合管道符 |
在实际工作中,我们很少拿 grep 单干,它最强大的形态是作为过滤器,截杀其他命令的输出。
管道符 | 的作用:把左边命令的输出结果,直接当成文本喂给右边的 grep 去筛选。
实战场景 1:排查进程 (最最最常用)
“系统好卡,nginx 进程还在跑吗?”
ps aux 会列出系统当前成百上千个进程,交给 grep 筛选出只带 nginx 字眼的行
ps aux | grep "nginx"
实战场景 2:精准查看端口占用
“8080 端口被谁占用了?”
netstat -tulnp | grep "8080"
实战场景 3:过滤历史命令
“我昨天敲了一个很长很复杂的 docker 命令,想不起来了。”
history | grep "docker run"
4. 新手最容易踩的 2 个“天坑”
❌ 坑 1:把“通配符”和“正则表达式”搞混
这是我们在讲 * 和 ? 时特意强调过的。
find -name "*.txt"里的*是通配符,代表任意字符。grep "a*"里的*是正则表达式,代表“前一个字符 a 出现 0 次或多次”。- 如果你想用
grep搜索真正的“任意字符”,请用.*(点加星号)。
❌ 坑 2:搜索进程时,把自己也搜出来了
当你执行 ps aux | grep "nginx" 时,输出结果往往有两条:一条是真正的 nginx 进程,另一条是你的 grep "nginx" 命令本身(因为这个命令执行时也成了进程,且名字里带有 nginx)。
- 老鸟的优雅解法:巧妙利用正则的方括号过滤掉 grep 自己:
(这样 grep 命令本身的名字变成了ps aux | grep "[n]ginx"grep [n]ginx,不再匹配正则[n]ginx,完美隐身!)
总结来说,
find 找的是“壳”(文件),grep 找的是“魂”(内容)。
top 指令
top 指令可以实时、动态地显示系统中各个进程的资源占用状况,类似于 Windows 的“任务管理器”。
要真正看懂 top 的输出,我们需要将其分为两个主要部分:上半部分的“系统全局汇总信息” 和 下半部分的“进程详细列表”。
第一部分:系统全局汇总信息 (Summary Area)
当你输入 top 并回车后,屏幕最上方的 5 行提供了系统的整体健康状况。
1. 第一行:系统运行时间和平均负载 (Uptime & Load Average)
top - 16:21:22 up 10 days, 4:05, 2 users, load average: 0.15, 0.08, 0.05
- 16:21:22: 当前系统时间。
- up 10 days, 4:05: 系统已经连续运行了 10 天 4 小时 5 分钟。
- 2 users: 当前有 2 个用户登录。
- load average: 系统平均负载,后面三个数字分别代表过去 1分钟、5分钟、15分钟 的系统平均负载。
- 关键指标:如果这个数值大于你的 CPU 核心数,说明系统处于超负荷状态,有任务在排队等待执行。
2. 第二行:任务/进程状态 (Tasks)
Tasks: 150 total, 1 running, 149 sleeping, 0 stopped, 0 zombie
- total: 系统中的总进程数。
- running: 正在运行的进程数。
- sleeping: 处于休眠状态的进程数。
- stopped: 被停止的进程数。
- zombie: 僵尸进程数(已终止但未被父进程回收的进程,如果数量过多需要引起注意)。
3. 第三行:CPU 状态 (CPU States)
%Cpu(s): 2.5 us, 1.0 sy, 0.0 ni, 96.0 id, 0.5 wa, 0.0 hi, 0.0 si, 0.0 st
这是排查性能问题时最关键的一行,各项指标均以百分比 (%) 显示:
| 缩写 | 全称 | 含义 |
|---|---|---|
| us | user | 用户空间占用 CPU 的百分比(正常情况下大部分 CPU 被这一项占用)。 |
| sy | system | 内核空间占用 CPU 的百分比。 |
| ni | nice | 改变过优先级的用户进程占用 CPU 的百分比。 |
| id | idle | 空闲 CPU 百分比(越高越好)。 |
| wa | wait | IO 等待占用 CPU 的百分比(如果过高,说明硬盘或网络 I/O 存在瓶颈)。 |
| hi | hardware irq | 硬件中断占用 CPU 的百分比。 |
| si | software irq | 软件中断占用 CPU 的百分比。 |
| st | steal | 虚拟化环境中被其他虚拟机“偷走”的 CPU 百分比。 |
4. 第四、五行:内存与交换空间 (Memory & Swap)
KiB Mem : 8044268 total, 1523400 free, 4200868 used, 2320000 buff/cacheKiB Swap: 2097148 total, 2097148 free, 0 used. 3400500 avail Mem
- Mem: 物理内存使用情况。包括总内存 (total)、空闲内存 (free)、已用内存 (used) 和用作缓存的内存 (buff/cache)。
- Swap: 交换空间(虚拟内存)使用情况。
- 关键指标:如果
Swap的used数值非常高并且频繁波动,说明物理内存严重不足,系统正在使用硬盘当内存用,会导致性能大幅下降。
- 关键指标:如果
第二部分:进程详细列表 (Task Area)
下半部分是一个表格,实时列出了每个进程的详细指标:
| 列名 | 含义说明 |
|---|---|
| PID | 进程 ID (Process ID),用于唯一标识一个进程。杀死进程时需要这个数字。 |
| USER | 运行该进程的用户名。 |
| PR | 优先级 (Priority)。 |
| NI | 静态优先级 (Nice Value)。负值表示高优先级,正值表示低优先级。 |
| VIRT | 虚拟内存 (Virtual Memory)。进程使用的虚拟内存总量。 |
| RES | 常驻内存 (Resident Size)。进程实际使用的、未被换出的物理内存大小(看内存占用主要看这个指标)。 |
| SHR | 共享内存 (Shared Memory)。进程使用的共享内存大小。 |
| S | 进程状态 (Status)。D=不可中断睡眠, R=运行, S=睡眠, T=停止, Z=僵尸。 |
| %CPU | 进程占用的 CPU 资源百分比。 |
| %MEM | 进程占用的物理内存百分比。 |
| TIME+ | 进程自启动以来占用 CPU 的总时间。 |
| COMMAND | 启动该进程的命令名称或命令行。 |
实操指南:Top 的常用快捷键(内部指令)
进入 top 界面后,系统会默认每隔 3 秒刷新一次。你可以通过按下键盘上的快捷键来改变显示方式或对进程进行操作(注意大小写敏感):
- 排序控制:
P(大写):按 %CPU 使用率排序(默认行为)。M(大写):按 %MEM 内存使用率排序(排查内存泄漏时常用)。T(大写):按 TIME+ CPU 累积使用时间排序。
- 视图控制:
1(数字 1):展开或折叠各个 CPU 核心的单独使用情况。c:切换显示命令名称 / 完整的命令行路径。V(大写):树形结构显示进程及其父子关系。
- 操作进程:
k:杀掉 (Kill) 一个进程。按下后输入你要杀死的进程PID,回车确认。r:重新设置一个进程的优先级 (Renice)。按下后输入PID,再输入新的优先级数值。
- 退出:
q:退出top程序。
常用命令行启动参数
除了直接输入 top,你还可以在启动时加上参数来满足特定需求:
top -d 1 -n 5
-d: 刷新的时间间隔
-n: 刷新的次数
q:退出
top -d 5:设置刷新间隔为 5 秒(默认是 3 秒)。top -u nginx:只显示属于用户nginx的进程。top -p 1234:只监控 PID 为1234的特定进程。top -n 10:刷新 10 次后自动退出(常用于配合脚本将结果输出到日志文件)。
💡 进阶建议:
虽然top是所有 Linux 系统的标配,但如果你喜欢更友好、色彩丰富且支持鼠标点击界面的工具,建议尝试安装htop。它是top的增强版,视觉体验和交互逻辑都得到了极大的提升。
htop指令
如果说 top 是系统监控的“毛坯房”,那么 htop 就是精装修后的“豪华套房”。它是 top 的增强版本,最大的特点是色彩丰富、支持鼠标点击、可以通过方向键上下左右滚动,并且对新手极其友好。
绝大多数 Linux 发行版默认不自带 htop,你需要先安装它(例如 sudo apt install htop 或 sudo yum install htop)。
启动 htop 后,你会发现它的界面主要分为三个区域。我们来逐一拆解:
第一部分:顶部的仪表盘 (Header Area)

这是 htop 最具辨识度的地方,它用直观的进度条代替了 top 枯燥的数字。
1. CPU 核心使用率条 (1, 2, 3, 4…)
数字代表你的 CPU 核心(如果有 4 核,就会显示 1 到 4)。进度条的颜色不仅仅是为了好看,它们代表了不同的 CPU 负载类型:
- <span style=“color:blue”>🟦 蓝色</span> (Low Priority):低优先级进程(即调整过
nice值的进程)占用的 CPU。 - <span style=“color:green”>🟩 绿色</span> (Normal):普通用户的正常进程占用的 CPU(最常见)。
- <span style=“color:red”>🟥 红色</span> (Kernel):系统内核占用的 CPU 时间。
- 提示:如果红色条经常满载,说明系统内核开销极大(例如高强度的 I/O 操作或驱动问题);如果是绿色满载,通常是你的应用程序(如 Java, Python, 数据库)在全力运算。
2. 内存与交换空间 (Mem & Swp)
- Mem (内存):
- <span style=“color:green”>🟩 绿色</span>:程序实际使用的内存 (Used)。
- <span style=“color:blue”>🟦 蓝色</span>:缓冲区 (Buffers)。
- <span style=“color:orange”>🟧 黄色/橙色</span>:缓存 (Cache)。
- 提示:Linux 的内存管理机制是“不用白不用”,所以蓝黄部分占满是正常的。只要绿色没有顶满,你的内存就还够用。
- Swp (交换空间):如果红色的条出现并快速增长,说明物理内存告急,系统开始疯狂读写硬盘了。
3. 右侧汇总信息
这里显示的内容与 top 第一行类似:
- Tasks: 进程总数和运行状态。
- Load average: 1分钟、5分钟、15分钟的平均负载。
- Uptime: 系统持续运行时间。
第二部分:中间的进程列表 (Process Area)
这里的列名(PID, USER, PRI, NI, VIRT, RES, SHR, S, CPU%, MEM%, TIME+)与 top 完全一致(排查内存依然重点看 RES 列)。
但 htop 在这里提供了几个杀手级优势:
- 完整命令路径:
COMMAND列默认会显示进程启动时的完整路径和所有参数,非常清晰。 - 上下左右滚动:你可以直接用键盘的
↑↓←→键来浏览所有进程,甚至看全超长的命令行参数(top只能看一屏)。 - 鼠标支持:你可以直接用鼠标点击列名(比如点击
%CPU)来进行排序,点击进程来选中它。
第三部分:底部的快捷键菜单 (Footer / Menu)
底部有一排类似 F1 到 F10 的快捷键提示,这是 htop 交互的精髓所在:
| 快捷键 | 功能 | 详细说明 |
|---|---|---|
| F1 (Help) | 帮助 | 查看详细的颜色说明和快捷键帮助。 |
| F2 (Setup) | 设置 | 进入设置菜单,可以自定义顶部仪表盘显示哪些内容、调整颜色主题、选择显示的列等。 |
| F3 (Search) | 搜索 | 输入进程名,按下回车即可高亮定位到该进程。 |
| F4 (Filter) | 过滤 | 输入关键字,列表中只显示包含该关键字的进程(排查特定服务如 nginx 时极其好用)。 |
| F5 (Tree) | 树形视图 | 🌟 强烈推荐! 按下后,进程会以父子树形结构展示。你可以清晰地看到哪个主进程派生了哪些子线程/子进程。可以配合 + 和 - 键折叠或展开。 |
| F6 (SortBy) | 排序 | 弹出一个侧边栏,让你选择按照 CPU、内存、PID 等任意列进行排序。 |
| F7 / F8 | 调整优先级 | 选中一个进程后,按 F7 减少 nice 值(提高优先级),按 F8 增加 nice 值(降低优先级)。 |
| F9 (Kill) | 杀进程 | 选中进程后按 F9,左侧会弹出一个菜单让你选择发送什么信号(默认是 15 SIGTERM 优雅退出,遇到杀不掉的可以选择 9 SIGKILL 强制击杀),回车执行。比 top 安全且直观得多。 |
| F10 (Quit) | 退出 | 退出 htop(也可以直接按 q)。 |
其他隐藏的神仙操作:
- 空格键 (Space):选中/标记一个进程。它的条目会变成高亮色。你可以标记多个进程,然后按
F9批量杀死它们。 - 大写
U:取消所有的标记。 - 小写
u:弹出一个用户列表,选择某个用户后,只显示该用户的进程。
总结: 了解了 top 的原理后,日常运维和排查问题时,请尽可能使用 htop。它的树形视图 (F5)、过滤功能 (F4) 以及直观的击杀菜单 (F9),能为你节省大量的排错时间。
zip 和 unzip
zip 和 unzip 是 Linux 系统中用于处理 .zip 格式文件的标准压缩和解压工具。
由于 .zip 格式在 Windows 和 macOS 上都能直接原生双击打开,所以在跨平台传输文件时,使用 zip 比 Linux 特有的 tar.gz 更加通用和方便。
第一部分:zip 指令(将文件/目录打包压缩)
基本语法:
zip [参数] [压缩包名称.zip] [要压缩的文件或目录路径]
🌟 最常用的实战场景
1. 压缩单个或多个文件
zip backup.zip file1.txt file2.log
将
file1.txt和file2.log压缩成一个名为backup.zip的文件。
2. 压缩整个目录(⚠️ 最易错点)
zip -r project.zip /path/to/project_folder/
注意: 如果要把目录里的内容递归打进 ZIP 包,通常需要加上
-r(recursive,递归)参数。不同zip实现对“只给目录名但不加-r”时的提示/结果可能不同,不能统一说一定会得到一个只包含空文件夹的压缩包;核心结论是:不加递归选项就不会把目录树中的文件完整递归打包。
3. 加密压缩(保护隐私数据)
zip -e secret.zip my_password.txt
加上
-e(encrypt) 参数后,系统会提示你连续输入两次密码。之后解压这个文件时就需要密码了。
4. 压缩并同时删除原文件(移动模式)
zip -m log_archive.zip *.log
把当前目录下所有的
.log文件打包进log_archive.zip,打包成功后,自动把原来的.log文件删掉,节省空间。
5. 安静模式压缩(不输出长串日志)
zip -r -q output.zip /large_folder/
如果文件夹里有一万个文件,默认压缩时屏幕会疯狂滚动。加上
-q(quiet) 可以让它安静地在后台干活。
第二部分:unzip 指令(解压缩文件)
基本语法:
unzip [参数] [压缩包名称.zip]
🌟 最常用的实战场景
1. 直接解压到当前目录
unzip project.zip
2. 解压到指定的文件夹中(最常用)
unzip project.zip -d /opt/my_app/
加上
-d(directory) 并跟上目标路径。如果目标路径不存在,unzip会自动帮你创建这个文件夹。
3. 不解压,只查看压缩包里有什么
unzip -l project.zip
-l(list) 参数可以列出压缩包内的文件列表、大小和日期,但不会实际提取文件。相当于“预览”。
4. 强制覆盖同名文件(免打扰模式)
unzip -o update.zip
如果解压目录里已经有同名文件,系统默认会停下来问你:是覆盖(y)、保留(n)还是重命名®?加上
-o(overwrite) 会直接静默覆盖所有同名文件,非常适合写在自动化脚本里。
5. 永不覆盖原文件(安全模式)
unzip -n update.zip
和
-o相反,-n(never overwrite) 遇到同名文件会直接跳过,只解压那些当前目录没有的新文件。
💡 避坑指南:Windows 传来的压缩包解压乱码怎么办?
这是因为 Windows 系统通常默认使用 GBK(CP936) 或 GB2312 编码打包中文文件名,而 Linux 默认使用 UTF-8 编码。解压时经常会出现乱码。
解决方法 1:指定编码格式解压
unzip -O CP936 windows_files.zip
-O CP936就是告诉 Linux,这个包是用 Windows 的中文编码压缩的,让linux以CP936的方式来解压
解决方法 2:使用更强大的解压工具 unar
如果 -O 参数不管用(某些旧版 Linux 不支持),强烈建议安装 unar。它会自动识别编码,可以说是乱码克星:
Ubuntu/Debian 安装: sudo apt install unar
CentOS/RHEL 安装: sudo yum install epel-release && sudo yum install unar
unar windows_files.zip
补充:SSH
SSH 的全称是 Secure Shell(安全外壳协议)。简单来说,它是一种加密的网络传输协议,最核心的作用是让你能够通过网络,安全地远程登录和管理另一台计算机(尤其是 Linux/Unix 服务器)。
你可以把它理解为一条“加密的专属隧道”,连接着你的电脑和远端的服务器。
为什么需要 SSH?
在 SSH 出现之前,人们通常使用 Telnet 或 rlogin 等协议来远程控制服务器。但这些老协议最大的致命伤是明文传输——你的账号、密码以及输入的每一条命令,在网络上都是以清晰可见的文本形式发送的,黑客通过网络抓包就能轻易窃取。
SSH 解决了这个问题。它在客户端和服务器之间建立了强大的加密连接,哪怕数据在传输过程中被黑客截获,他们拿到也是一堆无法破解的乱码,从而保证了数据的安全性。
SSH 的三大核心用途
- 远程命令行控制: 这是最常见的功能。你在本地终端输入命令,通过 SSH 发送到服务器执行,服务器再把结果传回你的屏幕。就好像你坐在那台服务器的键盘前一样。
- 安全文件传输:
基于 SSH 协议,衍生出了 SCP (Secure Copy) 和 SFTP (SSH File Transfer Protocol) 工具,用于在本地和远端服务器之间安全地上传或下载文件。 - 端口转发(SSH 隧道):
它可以把其他不安全的网络流量(比如数据库连接)包裹在 SSH 的加密通道中传输,既能加密数据,也能用来穿透某些防火墙限制。
SSH 是如何验证身份的?
当你尝试通过 SSH 连接服务器时,通常有两种方式证明“你是你”:
- 密码认证: 最基础的方式,输入服务器的账号和密码。但如果密码太弱,容易遭到暴力破解。
- 密钥认证(强烈推荐): 这是一种更高级、更安全的方式。你需要在本地生成一对“钥匙”(公钥和私钥)。你把公钥交由服务器保管,私钥自己妥善藏好。登录时,服务器会向你发送一段随机信息,你的电脑用私钥对其加密后发回,服务器用公钥解密验证。这个过程不需要手动输入密码,且极难被伪造。
基本使用方法
如果你在使用 Linux、macOS,或者 Windows 10/11(已内置 OpenSSH 客户端),你可以直接在终端(Terminal 或 PowerShell)里使用 SSH 命令连接服务器:
默认格式:ssh 用户名@服务器IP地址
ssh root@192.168.1.100
如果服务器的 SSH 端口不是默认的 22,可以使用 -p 参数指定端口
ssh -p 2222 admin@10.0.0.5
补充:rz/sz
🚀 Linux 终端轻量级跨平台传输神器:rz/sz 完全指南
在日常折腾 CentOS 7 轻量应用服务器(完全不是只能用于 CentOS 7!它几乎适用于所有的 Linux 发行版,甚至包括 macOS 和部分 Unix 系统。)、编写 C++ 代码或分析日志时,我们经常需要在本地 Windows/Mac 电脑和 Linux 服务器之间互传文件。
传统的做法是打开 Xftp 或 FileZilla 等专门的软件。而 rz 和 sz (所属软件包为 lrzsz)的强大之处在于:不需要额外开软件,直接敲命令就能在终端里弹窗传文件。 它们是运维和开发人员减少切屏操作、提升效率的“肌肉记忆”级工具。
一、 核心概念:谁在发送?谁在接收?
很多新手容易把 rz 和 sz 搞混。记住一个核心视角:以服务器端(Linux)为中心。
rz(Receive Zmodem):服务器 接收 (Receive) 文件。即从本地电脑 上传 文件到 Linux 服务器。sz(Send Zmodem):服务器 发送 (Send) 文件。即从 Linux 服务器 下载 文件到本地电脑。
二、 底层原理:什么是 Zmodem?
rz 和 sz 中的 z,代表的是 Zmodem 协议。这是一个诞生于上世纪 80 年代 BBS(电子公告板)时代的古老协议。
它的工作原理非常巧妙: 当你敲下 rz 或 sz 时,Linux 服务器会向你的终端(如 Xshell)发送一串特定的控制字符(握手信号)。支持 Zmodem 协议的终端软件一旦“嗅探”到这串特殊字符,就会自动拦截它们,并弹出一个图形化的文件选择窗口,然后在底层的 SSH 数据流中悄悄完成文件的二进制传输。
这就是为什么不需要开特定的端口,只要你能连上 SSH,就能用 rz/sz 传文件的原因。
三、 环境准备与安装
在绝大多数 Linux 发行版(包括 CentOS 7)中,默认没有自带这套工具。可以直接通过 yum 包管理器一键安装:
sudo yum install lrzsz -y
四、 实战演练与最佳实践
1. rz:上传文件到服务器
- 基本用法:在终端输入
rz并回车,终端软件会弹出一个本地文件选择窗口。选好文件后,它会传输到你当前所在的 Linux 目录(即pwd所在的路径)。 - 🌟 进阶组合参数(强烈推荐养成习惯):
rz -be-b(binary):明确要求按二进制方式传输。传压缩包(.tar.gz)、图片或编译好的可执行文件时,显式加-b是一种稳妥写法;不能简单概括成“rz/sz 默认一定是文本模式,所以所有二进制文件不加 -b 就极容易损坏”。-e(escape):对所有的控制字符进行强制转义。防止传输过程中被某些特殊字符干扰中断,保证文件完整性。-y(overwrite):如果服务器上已有同名文件,直接覆盖而不提示。
2. sz:下载文件到本地
- 基本用法:输入
sz server_file.log,终端会弹窗询问你要把文件保存在本地的哪个目录下。 - 🌟 进阶用法:
# 一次性下载多个文件 (例如你的 C++ 源码) sz main.cpp config.h # 搭配通配符下载当前目录下所有的压缩包 sz *.tar.gz # 安全下载(同样强烈建议带上二进制和转义参数) sz -be data.tar.gz
五、 高阶排错与避坑指南
在实际工作中使用 rzsz,你大概率会遇到以下几个经典问题:
-
敲下
rz后,屏幕疯狂打印乱码(如**B0100000023be50)并且卡死- 原因:你使用的终端软件(如 Windows 自带的 cmd/PowerShell,macOS 原生 Terminal,或 VSCode 内置终端)不支持 Zmodem 协议。服务器发出了握手信号,但你的软件听不懂,直接将其当成文本打印到了屏幕上。
- 解决办法:Windows 用户请换用 Xshell、SecureCRT、MobaXterm 等专业终端;Mac 用户如果使用 iTerm2,需额外配置
iterm2-zmodem触发器脚本。 - 卡死急救:按下
Ctrl + C通常无效,需狂按Enter键,或尝试按Ctrl + X几次来强行中断握手过程。
-
传上去的压缩包解压报错,或脚本运行报
syntax error- 原因:可能是传输模式、终端对控制字符的处理、ZMODEM 兼容性或链路中间层干扰了数据,不能一律归因于“默认文本模式”。
- 解决办法:养成肌肉记忆,永远加上
-b和-e参数(即rz -be和sz -be)。
-
在
tmux或screen等多路复用器中使用失败- 原因:
tmux会拦截和重定向标准输入输出,导致 Zmodem 的控制字符被破坏,传输极易挂起。 - 解决办法:尽量不要在 tmux 会话内使用。先
prefix + d退出 tmux,在纯净的 SSH 终端里传完文件,再 attach 回去。
- 原因:
六、 传输工具大比拼:什么时候该用谁?
在服务器运维中,传输工具各有千秋,rzsz 并非永远是最佳选择:
| 工具 / 协议 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
rz / sz |
临时互传几兆、几十兆的小脚本、代码文件、日志。 | 极致便捷,在支持 ZMODEM 的终端中可直接传输,无需另外建立文件管理界面。 | 不适合复杂目录同步和超大规模传输;具体文件大小能力受客户端、lrzsz 版本和协议实现影响。目录通常需要先打包。 |
| SFTP (Xftp/FileZilla) |
传大量碎文件、几百兆甚至几个 G 的大文件、可视化管理目录。 | 图形化拖拽,支持断点续传,传输稳定。 | 需要额外打开独立软件,操作时手要在键盘和鼠标间频繁切换。 |
scp / rsync |
Linux 服务器与服务器之间互传文件。 | 速度极快,系统原生自带,支持 -r 传输整个文件夹。 |
纯命令行操作较长,需输入目标 IP、绝对路径及密码。 |
💡 总结:
rzsz是 Linux 终端里的“瑞士军刀”。它干不了重活(传输超大文件),但用来处理手头零碎的单文件传输,其效率无可匹敌。配置好你的终端工具,把rz -be刻进 DNA 里,能为你省下无数次切换窗口的麻烦。
补充:xshell是终端?什么是终端?
在今天的语境下,“终端”(Terminal)、“命令行”(CLI)、“Shell”这几个词经常被混为一谈,导致很多初学者觉得概念非常混乱。
- Xshell 确实是用来远程连接云服务器的软件(它内置了 SSH 网络通信功能)。
- 但同时,Xshell 的本质也是一个“终端”(准确地说,叫“终端模拟器”)。
为了帮你把这些概念彻底理清,我们需要把时间稍微往前拨一点点。
一、 什么是“终端” (Terminal)?
要理解终端,我们得回到上世纪七八十年代。那个时候的计算机(大型机)非常昂贵,一个房间那么大,一台机器要给几十个人同时使用。
怎么实现同时使用呢?工程师们从主机上拉出去几十根线,连到各个工位上。工位上放着一个只有显示器和键盘、没有 CPU 和硬盘的铁壳子。
这个铁壳子只负责两件事:
- 把你按下的键盘字符,通过电线传给主机。
- 把主机算出来的结果,显示在屏幕上。
这个东西,就叫硬件终端 (Terminal)。它是数据输入和输出的“终点”,它本身没有任何计算能力,它就是一个传话筒和显示板。
二、 现在的“终端”变成了什么?
今天,我们的个人电脑(PC)自己就拥有了强大的 CPU 和显卡,我们不再需要那个笨重的铁壳子了。
但是,底层的操作系统(尤其是 Linux 和 Unix)依然保留了当年那种“接收字符 -> 处理 -> 吐出字符”的工作方式。为了兼容这种方式,工程师们编写了软件,在你的屏幕上画出一个黑框框,假装自己是当年那个“铁壳子”。
这种软件,就叫 终端模拟器 (Terminal Emulator)。为了省事,我们现在统称它们为“终端”。
这里要把几个概念分开:Xshell 和 macOS Terminal 属于终端/终端模拟器一类;Windows Terminal 或传统 Console Host 负责终端界面;而 cmd.exe、PowerShell 本身更准确地说是命令解释器/Shell。 终端主要负责字符输入输出和显示,Shell 才负责理解命令语法。
三、 谁才是真正处理命令的?(Shell)
终端不是命令行输入界面吗?”
更准确地说,终端负责字符输入输出与显示;命令行背后真正负责解析命令语法的“大脑”叫 Shell(壳程序)。常见的有 Linux 里的 bash、zsh,Windows 则有 cmd.exe、PowerShell 等命令环境。
终端(如 Xshell)和 Shell 是两个完全独立的东西:
- 终端 (Xshell):负责把你的按键动作变成字母,发给 Shell;再把 Shell 传回来的文字,染上颜色(比如把你敲的错误命令标红)显示在屏幕上。
- Shell (Bash):负责理解和解析你敲的文字。遇到
ls时,它通常会找到并启动ls外部程序;随后ls进程通过系统调用向内核请求读取目录内容,再把结果以文本形式写回终端。
四、 总结:Xshell 到底是什么?
回到你的最终疑问,Xshell 的定位其实是:终端模拟器 + SSH 网络客户端。
打个比方来解释你敲击一次键盘的全过程:
- Linux 服务器上的 Shell (Bash) = 远在天边的一位老外,他负责实际干活。
- SSH 协议 (网络) = 跨洋电话线,保证你们俩的通信安全、不被窃听。
- Xshell (终端) = 你手里的这部电话机。
电话机(Xshell)本身不提供业务服务,也不懂你们在聊什么。它只负责把你嘴里说的话(键盘输入)通过电话线(SSH)传给老外(Shell),然后再把老外说的话用扬声器(屏幕黑框)播放给你听。
所以,你既可以说 Xshell 是连服务器的工具(因为它插了电话线),也可以说它是一个终端(因为它就是个听筒/话筒)。理清了这个脉络,以后你再听到这些名词,就不会觉得绕了!
补充:scp-Secure Copy(安全拷贝)
scp 本质上是通过 SSH 传输文件,所以只要两端有相应的 SSH/scp 支持,就能用,不要求“两边都必须是 Linux”。
如果说 rzsz 是 Linux 传小文件的“指甲刀”,那么 scp 就是真正干重活的“大砍刀”。
scp 的全称是 Secure Copy(安全拷贝)。你可以把它理解为 cp(本地复制)命令的网络升级版。它基于 SSH 协议,这意味着只要你能通过 SSH 连上那台服务器,你就能用 scp 传文件,而且传输过程是全链路加密的,非常安全。
在你的服务器运维和 C++ 学习中,当你需要把几十兆的编译环境包、数百兆的日志、或者一整个层级复杂的项目文件夹传到 CentOS 7 服务器时,scp 就是你的首选。
一、 scp 的万能语法公式
记住这个核心逻辑:从哪里来(源路径) ➡️ 到哪里去(目标路径)。
scp [可选参数] [源头路径] [目标路径]
⚠️ 关键概念:如何表示“远程主机的路径”?
在 scp 中,表示另一台服务器上的路径有固定的格式,必须严格包含 用户名、IP地址(或域名)、冒号、具体路径:
格式:
用户名@服务器IP:/具体的/目录/或文件
示例:root@192.168.1.100:/opt/my_project/
二、 实战演练:最常用的 4 大场景
假设你的本地电脑(或 A 服务器)要和你的 CentOS 7 服务器(B 服务器,假设 IP 为 10.0.0.8)进行文件交互。
场景 1:把本地文件“推”到服务器(上传)
目标:把本地的
main.cpp传到服务器的/home/user/code/目录下。
scp main.cpp root@10.0.0.8:/home/user/code/
- 执行过程:敲下回车后,系统会提示你输入
root用户的密码。密码验证成功后,文件立刻开始传输。
场景 2:把服务器文件“拉”到本地(下载)
目标:把服务器上的
error.log下载到本地的当前目录。
scp root@10.0.0.8:/var/log/error.log ./
- 细节提示:注意最后的
./,它代表本地的当前所在目录。千万别忘了写目标路径!
场景 3:传输整个文件夹(极其常用)
目标:把本地的整个
C++_Project文件夹上传到服务器的/opt/下。
scp -r C++_Project/ root@10.0.0.8:/opt/
- 核心参数:
-r(recursive,递归)。和zip -r一样,涉及到文件夹的拷贝,必须加-r,否则scp会直接跳过该目录并报错。
场景 4:两台远程服务器之间互传(云端对拷)
目标:你当前坐在本地电脑前,指挥 A 服务器(10.0.0.8)把文件传给 B 服务器(10.0.0.9)。
scp root@10.0.0.8:/opt/data.sql root@10.0.0.9:/backup/
- 优势:
scp支持用两个远程路径描述服务器之间的拷贝。但实际数据是远端直传还是经由本机中转,取决于 OpenSSH/scp 的版本、协议模式和选项,所以不能把“远程到远程一定完全不经过本机”当成固定规则。
三、 高阶实用参数
除了 -r,你还需要掌握这几个能解决特定痛点的参数:
1. -P (大写 P):指定非标准 SSH 端口
这是新手最容易踩坑的地方!如果你的服务器为了安全,把默认的 22 端口改成了 2222,你必须用 大写的
-P来指定端口。
注意 P 是大写,且放在源文件前面
scp -P 2222 local.txt root@10.0.0.8:/tmp/
(💡 吐槽:Linux 设计上的一大反人类痛点就是:ssh 命令指定端口用的是小写 -p,而 scp 却偏偏用大写 -P。老兵也经常敲错。)
2. -p (小写 p):保留文件的原始属性
scp -p script.sh root@10.0.0.8:/opt/
传过去的文件会保留修改时间、访问时间以及最重要的权限(如可执行权限 chmod +x)。
3. -l (limit):限制传输带宽
如果你在白天业务高峰期传大文件,为了防止把服务器带宽占满导致网站卡顿,可以限速。
限制速度为 8000 Kbit/s (大约 1MB/s)
scp -l 8000 huge_file.tar.gz root@10.0.0.8:/tmp/
四、 避坑指南与进阶建议
-
静默覆盖,没有后悔药!
scp遇到目标路径下有同名文件时,不会有任何确认提示,会直接无情覆盖!在覆盖重要配置文件前,切记先在服务器上备份原文件。 -
如何避免每次都输密码?
频繁使用scp时,每次都要输长长的密码会让人崩溃。强烈建议配置 SSH 密钥对免密登录(使用ssh-keygen和ssh-copy-id)。配置好后,scp会像本地拷贝一样丝滑,无需人工干预,非常适合写在自动化部署脚本里。 -
如果网络不稳定,或者文件超大(几十GB)怎么办?
scp最大的缺点是不支持断点续传。如果传了 99% 时网络断了一下,你只能从头重传。- 进阶方案:如果遇到超大文件,建议使用
scp的老大哥 ——rsync命令。 - 示例:
rsync -avzP local_folder/ root@10.0.0.8:/opt/(它不仅支持断点续传,还能只传输修改过的增量部分,速度极快)。
- 进阶方案:如果遇到超大文件,建议使用
总结:
日常在本地电脑的终端里,记住 scp [本地文件] [账号@IP:/路径] 这个基本句式,加上处理目录必备的 -r,你就已经掌握了服务器跨网络文件调度的核心技能。
tar指令
tar(Tape Archive,磁带归档)是 Linux 系统中最老牌、最权威,也是日常使用频率最高的打包工具。
在 Linux 系统中,“打包”和“压缩” 其实是两个不同的概念,虽然我们经常把它们连在一起说。在 Linux 中,最经典的组合是先用 tar 打包,再用 gzip 或 bzip2 压缩
-
打包 (Archiving): 是指将一大堆文件或目录变成一个总的文件。这就像把散乱的衣服放进一个行李箱,体积并不会变小,但方便搬运。
-
压缩 (Compression): 是指通过算法减少文件的体积。这就像把行李箱里的衣服通过抽真空袋抽掉空气,体积会明显变小。
一、 tar 的“语法公式”
tar 命令的构成就像是搭积木,必须遵循以下结构:
tar [主操作] [压缩类型] [辅助参数] [目标文件名] [要处理的源文件/目录]
1. 主操作(必选其一,且只能选一个)
-c(Create):创建一个新的归档文件(打包)。-x(Extract):提取归档文件中的内容(解压)。-t(List):查看归档文件里有什么内容(不解压,只偷看)。
2. 压缩类型(可选)
tar 本身只负责“打包”(把多个文件拼成一个大文件),不负责“压缩”。 要减小体积,需要配合以下参数:
-z(gzip):使用gzip算法。最常用,速度和压缩率均衡,后缀通常是.tar.gz或.tgz。-j(bzip2):使用bzip2算法。压缩率比 gzip 高,但耗时更长,后缀通常是.tar.bz2。-J(xz):使用xz算法。压缩率最高,极其耗时,通常用于发布内核源码等对体积要求苛刻的场景,后缀通常是.tar.xz。
3. 辅助参数(高频使用)
-v(Verbose):显示详细的执行过程(屏幕上会滚动显示正在处理哪些文件)。-f(File):⚠️ 最重要参数 它的意思是“指定归档文件的名称”。它必须紧跟在你要创建或解压的文件名之前( -f 就像是一个“指路牌”,它的意思是:“👉 紧挨着我的下一个词,就是压缩包的名字!”)-C(大写 C, Change directory):专门用于解压时,指定解压到哪个目标目录。如果你想用一条 tar 命令就把压缩包解压到其他地方(而不是你当前所在的路径),就必须使用 -C(大写)参数来指定目标路径。
总结一下:
不用去死记硬背 tar 的所有参数。把 tar -czf (打包压缩) 和 tar -xzf (解压) 当作固定的单词刻在脑子里,遇到复杂的排除或路径需求时,再来查阅 --exclude 和 -C 即可。
二、 实战演练:最常用的 5 大场景
把上面的参数组合起来,就形成了我们日常敲击的命令。
场景 1:把目录打包并压缩(最常用)
目标:把
/var/www/html/project目录打包成project.tar.gz。
tar -czvf project.tar.gz /var/www/html/project
- 原理解析:
c(创建) +z(gzip压缩) +v(看进度) +f(指定文件名为 project.tar.gz)。
场景 2:解压到当前目录
目标:把
project.tar.gz解开,放在当前所在的路径下。
tar -xzvf project.tar.gz
- 💡 Pro Tip(高阶技巧):现代 Linux 系统的
tar已经非常聪明,它能根据文件后缀自动识别压缩格式。所以解压时,不管它是.gz,.bz2还是.xz,你都可以直接省略压缩参数,统一使用一招鲜:tar -xvf project.tar.gz
场景 3:解压到指定目录
目标:把压缩包解压到
/opt/app/目录下。
tar -xvf project.tar.gz -C /opt/app/
- 注意是大写的
-C,并且/opt/app/这个目录必须提前存在。
场景 4:开发者的痛点 —— 排除特定目录(如 node_modules / .git)
目标:打包一个前端或后端项目,但是不想把巨大的依赖库和版本控制文件夹打进去。
tar -czvf my_code.tar.gz --exclude='node_modules' --exclude='.git' ./my_project
把 ./my_project 目录压缩成 my_code.tar.gz,排除 node_modules 和 .git。
- 使用
--exclude='规则',可以跟多个。非常适合发版或备份纯源码。
场景 5:只查看内容,不解压
目标:别人发来一个压缩包,你想先看看里面有没有危险文件或套娃文件夹,然后再决定怎么解压。
tar -tvf unknown_file.tar.gz
三、 经典避坑指南(新手必看)
-
-f参数的位置错误(致命错误)- ❌ 错误写法:
tar -cvfz archive.tar.gz folder/ - 为什么错? 因为
-f后面必须紧跟文件名。上面的写法中,tar会以为你要创建的文件名叫作z! - ✅ 正确写法:
tar -czvf archive.tar.gz folder/
- ❌ 错误写法:
-
绝对路径警告 (Removing leading / from member names)
当你打包一个绝对路径(如/etc/nginx)时,tar会自动把路径最前面的斜杠/删掉(变成etc/nginx),并给你一个警告。- 这是在保护你! 如果它不删掉斜杠,将来你在任何地方解压这个包,它都会强行覆盖你系统根目录下的
/etc/nginx。去掉了斜杠,它就变成了相对路径,解压时只会安全地释放在你的当前目录下。
- 这是在保护你! 如果它不删掉斜杠,将来你在任何地方解压这个包,它都会强行覆盖你系统根目录下的
-
解压出“套娃”目录
有时你解压一个包,发现里面是home/user/project/src/...这么深的一长串目录。
如果只想提取最里面的一层文件,可以使用--strip-components=层数参数:剥离前面的两层目录结构 tar -xvf project.tar.gz --strip-components=2
补充:tar指令只能使用相对路径?zip呢?为什么?
纠正一个非常容易产生的误解:并不是它们只能使用相对路径,而是出于保护机制,它们在打包时会“强行”把你给的绝对路径变成相对路径。
你完全可以把绝对路径交给 tar 或 zip,比如 tar -czvf xxx.tar.gz /etc/nginx,它们完全能工作。但在真正打包存入文件的那一瞬间,它们会自动把最前面的那个斜杠 / 砍掉(变成 etc/nginx)。
常见的 zip 实现也通常会把输入的绝对路径以不带前导 / 的成员名写入归档,例如 /etc/nginx 常会记录成 etc/nginx/...。但 tar 与 zip 是两套不同工具,不能说内部行为逻辑“完全一样”,具体规则仍以各自实现和选项为准。
例如:
tar -czvf xxx.tar.gz /etc/nginx
GNU tar 通常会把开头的 / 去掉,压缩包内部记录为:
etc/
└── nginx/
所以如果在当前目录解压:
tar -xzvf xxx.tar.gz
会得到:
当前目录/
└── etc/
└── nginx/
如果指定:
tar -xzvf xxx.tar.gz -C /tmp
则得到:
/tmp/etc/nginx/
所以一句话:
tar 默认去掉绝对路径开头的
/,解压时把压缩包里的etc/nginx拼接到当前目录或-C指定目录后面。
🌟 核心问题:为什么要这么设计?
答案只有两个字:安全(防止毁灭性覆盖)!
为了让你直观理解,我们假设一个灾难场景:
假设 tar 或 zip 没有这个机制,允许保留绝对路径 /。
- 黑客的陷阱:有人在网上发了一个看似好用的工具包
awesome_tool.tar.gz。但实际上,他在这个压缩包里塞入了一个文件,路径被他故意写成了真正的绝对路径:/etc/passwd(这是 Linux 存放系统用户账号的核心文件)或者是/root/.ssh/authorized_keys(免密登录后门)。 - 你中招了:你把这个包下载到你的服务器上,随便找了个目录(比如
/tmp),开心地敲下了tar -xzf awesome_tool.tar.gz。 - 毁灭发生:因为压缩包里记录的是绝对路径
/etc/passwd,tar就会无视你当前的所在位置,直接冲到系统的/etc/目录下,把系统原本的账号文件强行覆盖替换掉!你的服务器瞬间瘫痪或被黑客接管。
🛡️ 现在的保护机制是如何生效的?
正是因为有这种巨大的安全隐患,GNU tar 默认会移除成员名前导的 /,并在解包时采用相应的安全处理;同时也提供 -P/--absolute-names 让明确知道风险的用户覆盖这一默认行为。所以这是安全默认值,不是“绝不允许”的死规矩。
当你打包 /etc/nginx 时,系统自动砍掉 / 变成 etc/nginx。
这样可以显著降低“归档成员用绝对路径直接覆盖系统文件”的风险。但解压不受信任的压缩包时仍要谨慎,因为还可能涉及 .. 路径、符号链接、文件覆盖等其他风险,不能理解成绝对不会造成任何威胁。
💡 进阶:如果我非要保留绝对路径怎么办?
Linux 永远会给真正知道自己在干什么的高级用户留“后门”。如果你做的是系统级的整机备份,解压时就是要让它覆盖原系统,你可以强制保留绝对路径。
- 对于
tar:使用大写的-P(或--absolute-names) 参数。- 强制打包绝对路径:
tar -czvPf backup.tar.gz /etc/nginx - 强制按绝对路径解压:
tar -xzvPf backup.tar.gz
(注意:如果不加-P去解压一个包含绝对路径的包,tar 依然会自动剥离前导斜杠来保护你)
- 强制打包绝对路径:
总结:
这是一种典型的安全默认设计。系统允许你输入绝对源路径去打包,但常见工具默认不会把前导 / 原样保存在归档成员名中,从而降低误覆盖系统绝对路径的风险。它不能保证未来解压一定只会安全地释放在当前目录。
bc指令
在 Linux 的 Shell 脚本世界里,有一个非常尴尬的痛点:默认的 Bash 环境只能做整数运算。
如果你在终端里输入 echo $((10 / 3)),它只会老老实实地返回 3。如果你想算 1.5 + 2.5,Bash 直接会报错。
这时候,bc (Basic Calculator) 就闪亮登场了。它不仅是一个支持任意精度的计算器,更是一门完整的微型编程语言。日常运维和写脚本时,凡是涉及到小数、进制转换、或者高级数学公式,都离不开它。
下面为你详细拆解 bc 指令的核心玩法和实战场景:
一、 基础玩法:交互式计算器
在终端直接敲下 bc,你就会进入它的交互模式:
[root@centos ~]# bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
在这个界面下,你可以直接输入算式并回车:
- 输入
1 + 2,回车输出3 - 输入
10^3(10的3次方),回车输出1000 - 输入
quit退出计算器。
💡 避坑提示(静默启动):
每次进 bc 都会打印一大堆版权信息(如上图)。你可以加上 -q (quiet) 参数:bc -q,这样就能清爽地直接开始计算了。
二、 高阶实战:管道传值(最常用的方式)
在实际写脚本时,我们不可能手动进入交互界面敲数字。最标准的姿势是用 echo 配合管道符 | 把算式“喂”给 bc。
1. 基础小数运算
echo "1.5 + 2.5" | bc
# 输出: 4.0
2. scale:控制小数精度(除法的灵魂)
默认情况下,bc 的 scale 值为 0,最明显的影响是除法默认不会保留小数部分。 这不等于 bc 只能处理整数输入;如果你希望除法等计算保留指定的小数位,可以设置内置变量 scale。
多个表达式之间用分号 ; 隔开:
# 默认情况
echo "10 / 3" | bc
# 输出: 3
# 指定保留 4 位小数
echo "scale=4; 10 / 3" | bc
# 输出: 3.3333
3. obase 与 ibase:无敌的进制转换器
在排查网络掩码、内存地址时,经常需要在二进制、十进制、十六进制之间反复横跳。bc 是最完美的转换工具:
obase(Output Base):输出进制(你想得到什么进制)。ibase(Input Base):输入进制(你提供的是什么进制)。
场景 A:十进制转二进制
echo "obase=2; 255" | bc
# 输出: 11111111
场景 B:十六进制转十进制
echo "ibase=16; FF" | bc
# 输出: 255
(注意:在 bc 中输入十六进制字母时,必须大写,否则它会当成变量名处理而报错。)
4. -l 参数:开启高级数学库 (Math Library)
如果你要算三角函数、对数,需要加上 -l 参数开启标准数学库。
加上 -l 后,scale 会自动默认变成 20 位小数。
# 计算 sqrt(2) (根号2)
echo "sqrt(2)" | bc -l
# 输出: 1.41421356237309504880
# 计算 sin(1) (需要加上 -l)
echo "s(1)" | bc -l
总结:
日常在终端随手敲个算术题,直接 bc 回车即可;写在自动化脚本里,牢记 echo "scale=小数位数; 算式" | bc 这个核心句式,你就能彻底摆脱 Bash 无法计算小数的痛苦了。
uname -r指令
在 Linux 世界里,这是一个出场率极高的基础指令。它的作用非常专一:打印当前系统正在运行的“内核发行版本号” (Kernel Release)。
一、 一行看似乱码的输出到底代表什么?
由于你之前提到过在使用 CentOS 7 服务器,当你在终端敲下 uname -r 并回车后,通常会看到类似下面这样的一串数字:
3.10.0-1160.el7.x86_64
这串字符包含了关于系统底层核心的巨大信息量。我们把它像切香肠一样切开来看:
| 字段 | 示例 | 详细含义 |
|---|---|---|
| 主版本号 (Major) | 3 |
Linux 内核版本号中的主版本字段。主版本号变化并不必然表示“内核架构发生根本性改变”;现代 Linux 的版本编号也受发布节奏和版本管理方式影响。 |
| 次版本号 (Minor) | 10 |
重大新特性的加入。(在早期 Linux 中,偶数代表稳定版,奇数代表开发版,不过现在的内核版本号规则已经放宽了。) |
| 修订号 (Revision) | 0 |
修复了 Bug 或增加了一些小的驱动支持。 |
| 编译/补丁号 | -1160 |
这是 RedHat/CentOS 官方自己加上的编号,代表他们对这个 3.10.0 内核打了几次安全补丁或做了几次重新编译。 |
| 发行版标识 | .el7 |
enterprise linux 7 的缩写,明确告诉你这是为 CentOS/RHEL 7 系列定制的内核。 |
| 硬件架构 | .x86_64 |
你的 CPU 架构。x86_64 代表普通的 64 位 CPU;如果是 aarch64 则代表 ARM 架构(比如苹果 M 芯片或某些云端 ARM 服务器)。 |
二、 什么时候必须用到 uname -r?
平时用系统的时候你根本不需要关心内核版本,但遇到以下几种硬核场景时,这个命令就是你的“通行证”:
- 安装底层软件(比如 Docker):
Docker 对内核版本有硬性要求(通常要求 CentOS 下的内核版本至少是3.10以上)。装 Docker 前第一步就是敲uname -r验明正身。 - 安装显卡或网卡驱动:
如果你要给服务器安装需要内核模块的 NVIDIA 显卡驱动,或者编译某个特殊的硬件网卡驱动,通常需要与当前运行内核的 ABI、头文件/开发包和构建环境保持兼容。很多外部模块会针对当前内核重新编译,因此不能简单理解成“版本字符串必须一丝一毫不差”。 - C++ 底层/系统级编程:
你在做 C++ 开发时,如果用到了 Linux 特有的系统调用(System Calls)或比如epoll等高级网络特性,不同版本的内核表现可能会有差异,你需要确认代码运行的环境。 - 排查系统漏洞(安全审计):
当爆出某个 Linux 内核级别的严重漏洞(比如“脏牛漏洞” Dirty COW),你需要立刻用uname -r检查自己的内核版本是否在受影响的名单内。
三、 容易混淆的“三大灵魂拷问”
很多新手会把这三个命令搞混,这里帮你一次性理清:
-
问:“我的内核版本是多少?”
👉 敲uname -r
(输出:3.10.0-1160.el7.x86_64) -
问:“我的 CPU 是 32 位还是 64 位的?”
👉 敲uname -m或者直接敲arch
(输出:x86_64) -
问:“我到底用的是 Ubuntu 还是 CentOS?系统是几 20.04 还是 7.9?”
👉 敲cat /etc/os-release或者cat /etc/redhat-release
(注意:Linux 的“发行版版本”和“内核版本”是两码事。CentOS 7.9 是发行版,它内部搭载的发动机是 3.10 内核。)
💡 终极偷懒技巧:
如果你记不住 -r 还是 -m,直接敲 uname -a (all)。它会把机器名、内核版本、编译时间、架构等所有底层信息一次性全部打印出来。
type
1. 命令简介
type 是 Bash 内建命令,用于查询命令的类型(Shell 将如何解释它)。知道命令类型可针对性获取帮助:如果某个命令是 Shell 内建命令,比如 cd、echo、read,它是 Shell 自己实现的,没有单独的可执行文件,所以通常用 help 命令名 查看帮助;如果是 外部命令,比如 ls、grep、find,它们本身是独立的程序,一般用 man 命令名 或 info 命令名 查看系统手册。因此可以简单记成:Shell 自己的命令看 help,系统里的独立程序看 man/info。
2. 五种命令类型
| 类型 | 说明 | 示例 |
|---|---|---|
| alias | 别名 | ls 常被别名为 ls --color=auto |
| keyword | Shell 保留关键字 | if、for、while |
| function | Shell 函数 | 用户自定义函数 |
| builtin | Shell 内建命令 | cd、echo、type |
| file | 外部命令(磁盘文件) | /usr/bin/date |
3. 语法与选项
type [-aftpP] name [name ...]
| 选项 | 作用 |
|---|---|
| -a | 显示所有匹配位置(含别名、内建、PATH路径) |
| -t | 仅输出类型单词(alias/keyword/function/builtin/file),无结果则无输出 |
| -p | 仅对 file 类型返回绝对路径,否则无输出 |
| -P | 强制在 PATH 中查找并返回绝对路径(忽略别名/内建) |
| -f | 排除 Shell 函数 |
4. 常用示例
查别名
type ls # ls is aliased to `ls --color=auto'
查内建
type cd # cd is a shell builtin
查关键字
type if # if is a shell keyword
查外部命令
type date # date is /usr/bin/date
仅输出类型
type -t for # keyword
显示全部类型(包含内建和外部)
type -a echo # echo is aliased... / builtin... / /bin/echo
强制查PATH(忽略内建)
type -P printf # /usr/bin/printf
5. 对比与注意
| 命令 | 适用对象 |
|---|---|
type |
所有命令(最全面) |
which |
仅外部命令 |
whereis |
外部命令 + 源码/手册 |
核心要点:
- Bash 查找顺序:别名 → 内建 → PATH 外部命令,
type默认显示最高优先级者。 - 返回码:全找到为 0,任一未找到为 1。
- 帮助:
help type(内建专用)。
linux中的热键
在 Linux 的命令行(Terminal / Bash)世界里,鼠标往往是派不上用场的。熟练掌握终端快捷键(热键),
Linux 终端的热键主要分为进程控制、光标移动、文本编辑和历史记录四大类。下面为你整理了最实用、最硬核的快捷键指南:
一、 救命神键(进程与屏幕控制)
| 快捷键 | 功能全称 | 实际作用与场景 |
|---|---|---|
Tab |
Auto-complete (自动补全) | 🌟 终端第一神键!防拼写错误、省力气的终极绝招。 1. 按一下:输入命令或文件名的前几个字母,系统会自动帮你补齐剩下的全写。例如输入 cd /et 按 Tab,会自动变成 cd /etc/。2. 连按两下:如果按一下没反应,说明有多个开头相同的选项(比如你输入 c),连按两下 Tab,系统会把你当前能敲的所有以 c 开头的命令或文件全部列出来供你参考。 |
Ctrl + C |
Interrupt (中断) | 强制中止当前正在运行的程序。 如果你用 top 看花眼了,或者跑了一个死循环脚本,无脑按它就对了。 |
Ctrl + D |
EOF (文件结束) | 向当前程序表示输入结束(EOF)。在 Shell 的空命令行上,它常会导致 Shell 退出,因此看起来类似 exit;在 cat > file、bc 等等待标准输入的程序中,它更多表示“后续没有输入了”。 |
Ctrl + L |
Clear (清屏) | 清空当前屏幕。 相当于敲了 clear 命令,让输入提示符重新回到屏幕最上方。 |
Ctrl + Z |
Suspend (挂起) | 通常向前台作业发送 SIGTSTP,把它暂停(Stopped)。它并不会自动在后台继续运行;可以用 bg 让它在后台继续,或用 fg 拉回前台继续。 |
Ctrl + S |
Stop (锁屏) | ⚠️ 新手巨坑! 这是“锁定屏幕输出”。如果你不小心按到了,终端会表现得像死机一样,敲什么都没反应。 |
Ctrl + Q |
Quit stop (解锁) | 解除锁屏状态。 如果你不小心按了 Ctrl+S 导致屏幕卡死,按 Ctrl+Q 就能瞬间满血复活! |
💡 核心建议:
在 Linux 里敲命令,不必每次都一个字母一个字母地死磕敲完;输入前几个字母后可以善用 Tab 补全。它不仅能提高速度,也能显著减少路径和文件名的拼写错误。但补全只能说明当前存在匹配项,不能保证你选中的目标 100% 就是你真正想操作的那个文件。
二、 瞬移神键(光标快速移动)
当你在终端敲了一行极长的命令,却发现最前面少敲了 sudo,如果你一直按左方向键 ← 挪过去,就太外行了。
| 快捷键 | 记忆口诀 | 功能说明 |
|---|---|---|
Ctrl + A |
Ahead | 瞬间跳到命令行的最开头。 |
Ctrl + E |
End | 瞬间跳到命令行的最末尾。 |
Alt + F |
Forward | 向前跳一个单词(英文单词跳跃)。 |
Alt + B |
Backward | 向后跳一个单词。 |
Ctrl + XX |
(连按两次 X) | 在光标当前位置和行首之间来回切换。 |
三、 剪刀手(快速删除与粘贴)
敲错命令不想一直按 Backspace(退格键)?这套快捷键能让你按词、按段落删除。
| 快捷键 | 功能说明 | 搭配绝技 |
|---|---|---|
Ctrl + U |
删除光标所在处,到行首的所有字符。(敲错一行,瞬间清空重写) | 配合 Ctrl+E 跳到末尾,再按 Ctrl+U 等于清空整行。 |
Ctrl + K |
删除光标所在处,到行尾的所有字符。 | - |
Ctrl + W |
向左删除一个单词。(敲错参数时最常用) | - |
Ctrl + Y |
粘贴刚刚被删除的内容。(Yank) | 刚才用 Ctrl+U/K/W 删掉的东西,其实被存进剪贴板了,按 Ctrl+Y 可以贴回来,相当于“剪切+粘贴”。 |
四、 时光机(历史命令召唤)
不要每次都狂按 ↑ 上方向键去找昨天敲过的长命令了。
Ctrl + R(Reverse Search) —— 🌟 终极神技- 用法:按下
Ctrl + R,然后开始输入你记忆中那条命令的几个字母(比如输入ssh或tar)。终端会自动从历史记录里把最近匹配的完整命令翻出来。 - 如果不满意:继续按
Ctrl + R,它会继续往前翻更老的匹配项。 - 确认执行:找到后,直接按回车就能执行;按右方向键
→可以把命令拉出来进行修改。
- 用法:按下
!!(连续两个感叹号)- 用法:执行上一条命令。
- 经典场景:敲了一长串命令发现权限不够报错,直接敲
sudo !!,系统就会自动帮你执行sudo 刚才那串长命令。
!$(感叹号加美元符)- 用法:调用上一条命令的最后一个参数。
- 经典场景:你刚敲了
ls -l /var/log/nginx/access.log,看了一眼没问题,接着想查看它,直接敲cat !$,系统就会自动执行cat /var/log/nginx/access.log。
第三章:shell命令以及运⾏原理
严格从 Linux 这个名称本身来说,它首先指 Linux 内核(kernel);日常又常用 Linux 指基于该内核构建的完整系统。一般用户不会直接随意操作内核内部,而是通过受控接口使用内核能力。Shell 是人与系统交互的重要方式之一,但不是用户程序访问内核的唯一途径;普通应用程序也可以通过系统库/系统调用请求内核服务。 如何理解?为什么不能随意直接操作 kernel?
从技术⻆度,Shell的最简单定义:命令⾏解释器(commandInterpreter)主要包含:
• 通俗地说,Shell 会解析使用者输入的命令,并据此执行内置功能或启动外部程序;需要内核服务时,再由 Shell/程序通过系统调用请求 kernel 处理。
• 同时,Shell 会组织命令的输入输出并把程序结果呈现给使用者;真正的结果数据也可能由外部程序直接写到终端。
我们来把 Shell 命令的运行原理,当成一场 “硬核相亲局” 来详细拆解。
1、 操作系统的广义与狭义
- 狭义地讨论 Linux/操作系统核心时: 常常重点指 “操作系统内核”(Kernel)。它负责进程调度、内存、文件系统、设备等核心资源管理。普通用户程序不能随意直接操作内核,而要通过系统调用等受控接口请求服务;不能简单说成内核只听得懂用户输入的“0 和 1”。
- 广义的操作系统: 包含了 内核 + 外壳程序(Shell/GUI)+ 必要的应用软件。我们平时买的 Windows 或装的 CentOS,都是广义的操作系统。
2、 故事背景与人物介绍(操作系统架构映射)
在这个叫作 Linux 村 的地方,有着严密的组织架构。我们先来对号入座:
- 如花(操作系统内核 Kernel / 狭义操作系统):
村里最核心的千金大小姐。她掌握着村里最关键的系统资源(CPU 调度、内存、文件系统、设备等)。普通用户程序不能随意进入内核空间,而要通过规定好的系统调用接口提出请求;不按接口和权限规则操作时,请求会被拒绝或程序出错,并不是“一说错话整个系统就必然崩溃”。 - 小李(普通用户 User):
村长家的傻儿子。他心里喜欢如花,有着强烈的需求(想让计算机执行各种任务,比如新建文件、跑个代码),但他极度不擅长和女生(内核)直接交流。 - 老李(超级管理员 Root):
村长。虽然他拥有最高的用户权限,可以进行更多管理操作,但 root 仍然是用户空间中的高权限身份;程序要使用内核能力时仍然要经过系统调用和内核权限检查。root 并不是因为“听不懂机器语言所以必须经过 Shell”,Shell 只是常用操作入口之一。 - 王婆(Shell 外壳程序 / 命令行解释器):
村里最专业的媒婆。她负责理解小李输入的 Shell 命令、变量、重定向、管道等语法,并决定执行内置命令还是启动外部程序。Shell 是重要的人机交互入口,但不是所有用户程序访问内核的唯一桥梁;普通应用程序也可以通过系统库/系统调用请求内核服务。
在 Linux 中,最著名的王婆叫作bash。 - 相亲全套服务(广义操作系统):
除了如花(内核),还包括王婆(Shell)、村口的布告栏(图形化界面 GUI)以及村里的各种基础设施(应用软件),它们加在一起构成了完整的 Linux 系统。
3、 运行原理解析(乡村相亲的三大名场面)
当小李坐在电脑前,敲下了一行命令并按下回车,故事是这样展开的:
🎬 场景一:相亲顺利(执行正确的命令)
操作: 小李输入
ls /usr/bin/bash
- 下达指令: 小李鼓起勇气对王婆说:“王婆,我喜欢如花,你想办法帮我查查她的底细(列出目录信息)!”
- 媒婆解析: 王婆(Shell)接收到指令后先解析命令,发现
ls是外部程序,于是按照命令查找规则找到并启动ls。 - 外部程序请求内核:
ls进程运行后,通过文件系统相关系统调用向内核请求读取/usr/bin/bash的属性信息。 - 传回喜讯:
ls把读取到的信息格式化后写到标准输出,终端负责把文字显示在屏幕上。Shell 等待该命令完成后继续接受下一条命令。小李如愿以偿,相亲顺利!
🎬 场景二:胡言乱语(Command Not Found)
操作: 小李因为太紧张,敲错了字,比如敲成了
小帅或者不存在的命令。
- 胡乱提要求: 小李结结巴巴地对王婆说:“王婆,我想跟小帅表白!”
- 王婆查户口: Shell 会先按自己的规则处理别名、函数、内置命令等;如果需要寻找外部程序,再翻开 花名册(环境变量
$PATH) 依次查找可执行文件。最终都没有找到时,就会认为命令不存在。 - 果断拦截: 王婆为了保护如花免受骚扰,直接把小李拦在门外,甩给他一句冷冰冰的话:
小帅是谁呀?command not found!(命令找不到!)
这就是王婆作为外壳的“保安”作用,变相地保护了内核。
🎬 场景三:王婆的终极绝招 —— “实习生替死鬼”(子进程机制)
这是理解 Unix/Linux 中 Shell 启动外部程序时非常重要的一类机制。
小李的要求有时非常复杂,甚至很危险(比如运行一个有 Bug 可能会导致死机的脚本,或者向如花进行一场极其激烈的硬核表白)。
如果王婆(主 Shell)亲自去帮小李传这个话,万一惹怒了如花,如花一巴掌扇过来把王婆打死了怎么办?
王婆要是死了,小李的屏幕就彻底黑了(终端掉线),这绝对不行!王婆可是个老江湖。
于是,王婆使出了绝招:
- 招募实习生(Fork): 当需要执行外部程序时,在经典 Unix/Bash 模型中,Shell 通常会通过
fork创建一个 “实习生媒婆”(子进程 Child Process),再由子进程执行外部程序。Shell 内置命令通常直接在当前 Shell 中执行,不是所有命令都必然 fork;现代实现某些场景还可能使用posix_spawn等机制。 - 实习生去踩雷(Exec): 王婆把小李的情书(程序指令)塞到实习生手里,让实习生去见如花(执行外部程序)。
- 王婆喝茶等待(Wait): 王婆本尊在外面搬个小板凳坐下,进入休眠等待状态。
- 两种结局:
- 结局 A(成功): 实习生和如花沟通得很顺畅,把结果带回来,实习生下班(进程结束)。王婆本尊站起来,把结果交给小李。
- 结局 B(爆炸): 小李的情书里写满了 Bug,如花大发雷霆,直接把实习生打死了(Segmentation Fault,程序崩溃退出)!
核心亮点就在结局 B: 因为被打死的是“实习生”,王婆本尊毫发无损!此时,王婆只要淡定地在屏幕上打印一个报错信息,然后笑眯眯地问小李:“刚才那个实习生殉职了,小李,你还有其他吩咐吗?”
💡 总结
在这个“乡村爱情故事”中,我们看到了 Shell(王婆)存在的终极意义:
- 命令解释器: 负责解析用户(小李)输入的命令语法,执行内置命令或启动外部程序,并组织输入输出。
- 交互与错误处理: 对找不到的命令、语法错误等在用户空间直接给出提示。真正的权限隔离和系统安全边界主要由内核的用户态/内核态、权限检查等机制提供,Shell 本身不是安全防火墙。
- 进程隔离机制(Fork & Exec): 外部程序通常在独立进程中运行,因此普通程序崩溃时一般不会直接把交互式 Shell 一起带崩,这提高了隔离性。但它不能保证终端或整个系统“绝对稳定”,资源耗尽、驱动/内核 Bug 等仍可能影响系统。
用户不需要直接操作内核内部,Shell 为用户提供了方便的命令交互入口;真正保护内核和系统资源的核心机制仍然是内核态/用户态隔离、系统调用接口和权限检查等。
补充:shell外壳是不是就是shell命令行或者图形化界面?
Shell 可以理解为用户与操作系统之间的一层“命令解释外壳”,但在 Linux/Unix 日常语境中,Shell 通常特指 Bash、Zsh、sh 这类命令行解释器,而不是所有图形界面;命令行界面 CLI 和图形界面 GUI 都属于用户与系统交互的上层方式,只是 Shell 属于其中的命令行这一类。终端负责显示和接收输入,Shell 负责解析你输入的 ls、cd、mkdir 等命令,再调用程序或系统接口完成操作;GUI 则通过窗口、图标、鼠标点击完成类似的交互。因此,平时说“Shell 命令”“Shell 脚本”,默认指的都是命令行解释器这一套。
用户与系统交互的上层界面常见有命令行和图形化两种形态;其中 Bash/Zsh 这类 Shell 属于命令行解释器。
1. 命令行 Shell (CLI - Command Line Interface)
- 表现形式: Shell 通常运行在终端/终端模拟器中,你通过文本输入一行行命令(比如
ls、cd、mkdir)。终端黑框是输入输出载体,Shell 才是解释命令的程序。 - 常见的“命令行王婆”: 在 Linux 里叫
bash、zsh、sh;在 Windows 里就是cmd.exe或PowerShell。 - 相亲比喻: 这种办公模式比较传统。老李必须手写一封格式极其严格的情书(敲命令),稍微写错一个字,王婆就会把信退回来报错。虽然门槛高,但效率极高,指哪打哪。
2. 图形化用户界面 (GUI - Graphical User Interface)
- 表现形式: 就是我们最熟悉的电脑桌面。有鼠标、有图标、有窗口、有回收站。你在桌面上双击打开一个文件夹,其实底层也是在向内核发指令。
- 常见的“图形化王婆”: 在 Windows 里叫
Windows 桌面 (Explorer.exe);在 Linux 里通常是GNOME或KDE;在手机里就是你的 Android/iOS 桌面启动器。 - 相亲比喻: 这是一种现代化的办公模式。王婆手里拿着一个 iPad 电子画册(桌面),老李不需要会写字,只要伸出手指在 iPad 上“点一下”(鼠标双击),王婆就会立刻明白老李的心意,转身去通知如花干活。门槛极低,老少皆宜。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
命令正常运行


所有评论(0)