From af350c1da274e7f197b83c1c809e8f4fa80c254c Mon Sep 17 00:00:00 2001 From: chn Date: Sun, 17 Nov 2024 23:56:05 +0800 Subject: [PATCH] add blog --- content/blog/linux-guide.md | 409 ++++++++++++++++++++++++++++++++++++ 1 file changed, 409 insertions(+) create mode 100644 content/blog/linux-guide.md diff --git a/content/blog/linux-guide.md b/content/blog/linux-guide.md new file mode 100644 index 0000000..f89efa2 --- /dev/null +++ b/content/blog/linux-guide.md @@ -0,0 +1,409 @@ +--- +title: 'Linux 教程新手篇(草稿)' +date: 2024-11-16T22:13:37+08:00 +--- + +在做实验,需要每五分钟去调整一下仪器,但中间的五分钟有点无事可做。 +所以来写一下这个东西得了。 + +原本是想要写个给同专业的同学看的教程,写了一点觉得,诶,我为啥不写成更通用的教程呢。 +于是就这样做吧。 + +## 顶层设计 + +我的想法是这样的:按照两个部分去写,一个新手篇,一个进阶篇。 +不管哪个部分,内容都不会限制于 Linux,甚至 Linux 的内容可能只占一半。 +其实很多人并不是不会用 Linux 而是对缺少对操作系统的一般认识, + 就像看到自己电脑有 C D E F 盘,就以为电脑里真的插了四个硬盘一样。 +另一个常见的误区就是不区分内存和外存。类似此类的问题。 +要真的只是某个 Linux 上的问题不会,反而还都是少见的。 + +暂时的计划是这样的,新手篇的内容包含: +* 什么是 shell?(和 windows 的桌面一个作用,只是变成了敲命令) +* shell 里命令的格式是怎样的?(第一个是要执行的程序,之后的是参数) +* 常用的几个命令。 +说白了就是围绕 bash 介绍一些基础用法。 +这其实是相当繁琐的事情,但又是很基础的,不得不介绍,新手接触 Linux 的话也不可能绕过。 + +进阶篇的内容会多一些杂一些,包括: +* 冯诺依曼架构(计算机的硬件组成,至少把内存和外存分清楚吧) +* 操作系统的结构(内核和用户态程序的区别) +* 分区、文件系统、挂载,这些概念是指什么;linux常见的几个文件夹里放什么。 +* 程序和进程间通信(环境变量,信号,输入输出,返回值) +* shell 中一些内置命令,以及重定向、通配符等。 +* 常用的工具(shell 中常用的外置命令)。 +* 用户、文件所有者和权限。 +* 网络协议,NAT。 +* 编程时常用的概念(编译、链接、解释器等)。 + +在这之后就区分“专业”了,例如做科学计算的话,需要了解: +* 常用的并行计算协议(MPI,OpenMP,CUDA,OpenCL,SYCL)。这里其实我也缺一些知识,我需要自己先把自己的疑惑解决了再写。 +* 队列系统。但是我已经写过了。 + +当然也可以写一些其它的。但这些要么已经有人写过了写得很好我受益匪浅,我觉得不需要我来写(例如 nix),要么我自己也不熟(例如 docker)。 +总之,写个屁。 + +我想要达成的目标是这样的: +* 对于普通用户: + * 读完新手篇,就可以着手做计算了。 + * 读完进阶篇,就大致知道自己遇到常见的错误时如何排查,或者如何向别人描述问题了。 +* 对于管理员: + * 全读完,就可以开始学习怎么搭服务器了(遇到问题时,知道去哪里找答案了)。 + +不只是做计算,如果有想自己折腾 Linux 的,这也算是一个比较好的入门;但我会侧重于前者,主要是后者的话,场景太杂,我根本不知道该写啥。 + +所以说应该会写四个部分: +* 速查篇,列个表格,满足某些人“我要一口吞个大象”的心态,他吞不下是他的问题,我给他把大象放到这里。 +* 新手篇,口语化地带领读者实践一圈。 +* 进阶篇,讲基础的、常见的、重要的、但是又往往被忽略的概念,为了给进一步向前走做准备。 +* 专业篇,指科学计算,这一部分更偏向于我自己的整理而不是科普给别人。 + +## 那就开始吧 + +下面是新手篇。 + +### 那个黑框框里是什么? + +先考虑这个问题:在 Windows 电脑上,如何删除一个文件?步骤是这样的: +1. 启动**资源管理器**(就是“我的电脑”或者“此计算机”)。 +2. 打开你要删除的文件所在的文件夹。 +3. 右键点击文件,选择删除。 +在这个过程中,“资源管理器”或者“exploer.exe”承担了这样一个“传话筒”的角色: + 它接受你下达的指令(打开某个文件夹,删除某个文件,等),并将这个指令传达给系统中那些更底层的程序(即内核)去执行。 + +通过 PuTTY 或者别的什么程序连接到服务器后打开的那个黑框框,就是 Linux 系统中的“资源管理器”[^2],它的名字叫“bash”[^3]。 +它的作用与 Windows 的资源管理器是一样的:你可以给他传达删除某个文件、启动某个程序等指令,它就会把这个指令传达给系统的更底层去实际执行。 +学习 Linux 的第一步,就是学习如何给 bash 下达指令,例如打开某个文件夹、删除某个文件、启动某个程序等。 +至于其中的细节(例如 bash 是如何与内核交互的,为了删除一个文件内核又需要做什么),暂时不需要关心。 + +类似于 Windows 的资源管理器或 bash 这样的程序被称为“shell”,也就是“壳”, + 这是指它把系统中复杂的细节包装了起来,让用户可以简单地进行删除文件、打开文件夹等操作,大多情况下不用关心细节。 + +### 几个常见的命令 + +学习英语的第一步并不是钻研语法,而是学习最常用的几句话大致怎么说。学习 bash 也是一样的。 +下面是几个最常用的命令,你可以跟着操作一遍,就大致知道这些命令该怎么用了,同时也可以理解 Linux 上一些基本的概念。 + +{{< callout type="warning" >}} + 如果你是在学校超算(jykang)上跟着操作,那么一定要小心,每一次回车前都要仔细检查输入的命令是否正确,千万不要误操作。 +{{< /callout >}} + +#### `pwd` + +在 Windows 的资源管理器中,可以在窗口上部、标题栏下方的地址栏中看到现在打开的是哪个文件夹(称为当前的“工作目录”); + 而在 bash 中,可以使用 `pwd` 命令查看工作目录的路径(`pwd` 是“print working directory”的缩写): +```bash +pwd +``` +将 `pwd` 输入到黑框框中,然后按回车键,它就会输出一行文字,告诉你现在在哪个文件夹下。例如,它输出的结果可能是: +``` +/home/chn +``` +这表示你现在打开的是“**根目录**”下的 `home` 文件夹下的 `chn` 文件夹。 + +要解释“根目录”的意思,需要先提到一个 Linux 与 Windows 的不同之处: + Windows 中往往逻辑上有多个“磁盘”(C 盘、D 盘、E 盘等),而 Linux 中**逻辑上**只有一个“磁盘”,这个“磁盘”就是所谓的“根目录”。 +当然,这个“根目录”中的内容完全可能实际是分布在多个硬盘上存储的(例如,某几个文件夹在一个硬盘上,另外几个文件夹在另一个硬盘上), + 但这些细节初学者并不需要关心,这些文件实际上存储在哪里对初学者来说操作起来完全没有区别。 + +你还会发现另外一个区别:在 Windows 中,文件夹之间的分隔符是反斜杠 `\`,而在 Linux 中,文件夹之间的分隔符是斜杠 `/`。 + +还有第三个重要的区别你可能没有发现: + 在 Windows 中,文件和文件夹名字是不区分大小写的,也就是说无法在同一个文件夹下新建 `aaa.txt` 和 `AAA.txt` 两个文件; + 而 Linux 中文件和文件夹名是区分大小写的,`aaa.txt` 和 `AAA.txt` 完全可以同时存在。 +不仅仅是文件名,整个 Windows 的习惯都是不区分大小写的,而整个 Linux 的习惯则都是区分大小写的。 +总之,我们之后学习的所有命令、各种参数等,它们都是区分大小写的; + 如果你在哪里看到过某某命令不区分大小写的说法,那大概率是 Windows 的特色,不能不经确认套用到 Linux 上。 + +#### `ls` + +使用 `ls` 命令(“list”的缩写)列出当前目录下,名字不以 `.` 开头的文件和文件夹(也就是不隐藏的文件和文件夹): +```bash +ls +``` + +在 Windows 上,一个文件或者文件夹是否是隐藏的,是它的一个属性(右击-属性中,可以修改); + 但在 Linux 上,通常直接把名字以 `.` 开头的文件或文件夹称为“隐藏文件”或“隐藏文件夹”,并通常用来存放程序的设置等用户平时不需要修改的内容。 +隐藏的文件与不隐藏的文件本质上并没有什么不同,这只是一种习惯性的用法。 + +要列出当前目录中所有的文件和文件夹(包括隐藏的),可以使用 `ls -a` 命令(“a”是“all”的缩写): +```bash +ls -a +``` +你一定可以看到两个特殊的文件夹:一个名字叫 `.`,令一个叫 `..`。我稍后会介绍它们是什么。 + +要列出别的地方的文件夹里的内容(而不是当前文件夹),可以在 `ls` 命令后面加上这个文件夹的路径,例如: +```bash +ls / +``` +这会列出根目录下的文件和文件夹(不包括隐藏的)。 +```bash +ls /home/chn +``` +这会列出 `home` 文件夹下的 `chn` 文件夹中的文件和文件夹(不包括隐藏的)。 +当然,如果你没有权限看这个文件夹里的内容的话,这个命令就会报错,但也不会有什么坏处,也不会影响之后的命令。 + +你可以按照自己的兴趣,随意列出几个文件夹的内容,探索一下服务器上有什么东西。不会把服务器搞坏的,不用担心。 +也可以将 `ls` 列出来的东西与 WinSCP 对照者看一下,确认它们是一样的[^4]。 + +{{< callout type="info" >}} + 如果你尝试列出 `/nix/store` 或者 `.node_modules` 的文件夹的内容,你的 shell 可能会卡死,这只是因为里面的文件太多了。 + 你可以耐心等待它列完,也可以按 `Ctrl + C` 来终止这个命令,实在不行就重启 PuTTY。 +{{< /callout >}} + +#### `mkdir` + +使用 `mkdir` 命令(“make directory”的缩写)在当前目录下创建一个新的文件夹: +```bash +mkdir test +``` +这样就在当前目录下创建了一个名为 `test` 的文件夹。 + +#### `cd` + +使用 `cd` 命令(“change directory”的缩写)切换当前目录(也就是,打开别的文件夹): +```bash +cd test +``` +这样就进入了当前目录下的 `test` 文件夹。 + +如果要返回上一级目录,可以使用 `cd ..` 命令: +```bash +cd .. +``` + +还记得前面提到的 `.` 和 `..` 这两个特殊的文件夹吗?其实 `..` 就是指上一层文件夹,而 `.` 就是指同一层文件夹。 +例如,下面几个目录的写法,它们之间是完全等价的: +```bash +/home/chn +/home/chn/. +/home/chn/./././././. +/home/../home/chn +/home/chn/../../home/chn +/home/././chn/.././chn +``` + +目前为止,很多命令都需要用到文件或文件夹的路径。 +实际上,文件或者文件夹的路径有两种写法,大多数情况下(包括基础篇中提到的所有命令但不仅限于),两种写法都可以,完全等价: +* 直接写当前目录下的文件或文件夹的名字,例如 `cd test` 或者 `cd ./test` 或者 `cd ./test/../test` 等,这种写法称为相对路径; +* 写完整的路径(以 `/` 开头的路径),例如 `cd /home/chn/test`,这种写法称为绝对路径。 +但也存在一些例外,其中一个重要的例外在基础篇的最后会提到。 + +#### `echo` + +使用 `echo` 命令输出一行文字: +```bash +echo Hello +``` +这样就输出了一行 `Hello`。——这有什么用呢? + +可以使用下面这个命令,在当前目录下创建一个名为 `file.txt` 的文件,并在这个文件中写入一行 `Hello` + (如果这个文件之前已经存在,其中的内容会被覆盖): +```bash +echo Hello > file.txt +``` +如果你想在这个文件中追加一行 `World`(之前的内容不会被覆盖),可以使用下面这个命令: +```bash +echo World >> file.txt +``` +这里的 `>` 和 `>>` 是两个特殊的符号,被称为“重定向符号”, + 它表示把之前的命令的标准输出(也就是,原本会输出到屏幕上的内容[^5])重定向到某个文件中。 +`>` 表示覆盖,`>>` 表示追加。 +它们的用法不仅限于 `echo` 命令,而是可以用在任意的命令上,例如 `ls > file.txt` 就会把 `ls` 命令的输出重定向到 `file.txt` 文件中。 + +#### `cat` + +使用 `cat` 命令(“meow meow meow”的缩写[^6])查看一个文件的内容: +```bash +cat file.txt +``` + +你当然可以“活学活用”,把 `cat` 命令的输出重定向到另一个文件中,这样就变相实现了文件的复制: +```bash +cat file.txt > file2.txt +``` + +类似于这样的“活学活用”在 Linux 中其实比比皆是。就像一篇光鲜亮丽的 sci 论文背后总是充满了科研现实的苟且。 + +#### `cp` + +这个才是正经用来复制文件的命令(`copy` 的缩写): +```bash +cp file.txt file2.txt +``` + +如果要复制的是文件夹,需要加上 `-r` 参数(“r”是“recursive”的缩写[^7]): +```bash +cp -r test test2 +``` + +#### `rm` + +使用 `rm` 命令(“remove”的缩写)删除一个文件: +```bash +rm file.txt +``` +这样就删除了当前目录下的 `file.txt` 文件。 + +要删除文件夹,需要加上 `-r` 参数(与 `rm` 相同): +```bash +rm -r test +``` +这样就删除了当前目录下的 `test` 文件夹。 + +### bash 的基本语法 + +#### 按行分割 + +英语分为一个个句子,以句号作为结尾;输入给 bash 的指令是一条一条的,通常以换行作为结尾。 +例如,输入下面的命令: +```bash +cd test +rm file.txt +cd .. +``` +bash 会把它作为三条指令来执行。 + +#### 命令与参数 + +英语的一句话中,主语后接的是谓语、宾语; + bash 的一条指令中,按照空格分隔,第一个单词是要执行的命令,之后如果还有内容的话,只要不是一些特殊的符号(例如 `>`),就是这个命令的参数。 +例如在下面的命令中: +```bash +rm file.txt +``` +`rm` 是要执行的命令,`file.txt` 是这个命令的参数。 + +在按照空格分割的时候,连续的多个空格与单个空格是等价的,例如下面的命令: +```bash +rm file.txt +``` +与上面的命令是等价的。 + +如果命令或者参数中包含空格,可以用双引号或单引号把它括起来,例如: +```bash +rm "file with space.txt" +``` +这样 bash 就知道 `file with space.txt` 是传给 `rm` 的一个参数;而不是把它分成 `file`、`with`、`space.txt` 三个参数。 +也就是说,要删除的是名为 `file with space.txt` 这一个文件,而不是名为 `file`、`with`、`space.txt` 的三个文件。 + +每个命令可以接受参数的个数不同。 +例如,删除文件的命令通常需要至少一个参数(你得告诉它删除哪个文件),至多则不限制; + 而复制文件的命令通常至少需要两个参数(你得告诉它将哪个文件复制到哪里)。 + +#### 内置命令与外部程序 + +`cd`、`pwd` 等命令并不会启动 bash 以外的程序:它们是 bash 内置的命令,它们的作用是调整或者输出 bash 本身的状态, + 相当于查看或者修改 Windows 的资源管理器本身的设置,而不是要 bash 做什么实际的事情。 +而 `rm file.txt` 这个命令则实际上是启动了一个放置在特定位置[^8]、名为 `rm` 的程序,并将 `file.txt` 作为参数传给它。 +`mkdir`、`cp` 等命令也是这样的。 + +那么,如果要运行一个程序,而这个程序又不放在类似于 `/usr/bin` 这样的特殊位置,应该怎么调用呢?有三个办法: + +* 修改环境变量。新手篇不会介绍这个办法,因为新手很容易把环境变量搞乱。 +* 使用绝对路径调用程序。例如,这个程序放置在 `/home/chn` 文件夹下,名字叫 `myprogram`,那么可以这样调用: + ```bash + /home/chn/myprogram + ``` + 如果要传递参数给这个程序,也是一样的写法: + ```bash + /home/chn/myprogram arg1 arg2 + ``` +* 使用以 `.` 开头的相对路径调用程序。必须使用以 `.` 开头的相对路径(`..` 也可),这就是之前提到的那个重要的例外。 + 例如,这个程序放置在当前文件夹下,名字叫 `myprogram`,那么可以这样调用: + ```bash + ./myprogram + ``` + +#### 管道与 `grep` 命令 + +除了之前介绍的 `>` 和 `>>` 之外,还有一个非常常用的特殊符号,叫做“管道符号” `|`,就是键盘上那个竖线。 +它的作用是把一个命令的标准输出重定向到另一个命令的“标准输入”(就是这个命令原本看到的用户的输入[^9])。 + +管道符号经常和 `grep` 命令一起使用,`grep` 命令的作用是在输入中查找某个字符串,并输出包含这个字符串的行。 +例如: +```bash +cat OUTCAR | grep "Total energy" +``` +这个命令可以输出 `OUTCAR` 文件中包含 `Total energy` 的行。 + +`grep` 有一些常用的参数,包括: +* `-i`(“ignore case”的缩写):匹配时忽略大小写; +* `-An`(“after”的缩写): 除了输出匹配的内容,还输出匹配内容的后几行; +* `-Bn`(“before”的缩写): 除了输出匹配的内容,还输出匹配内容的前几行; +* `-v`(“invert match”的缩写):输出不匹配的行,而不是输出匹配的行。 + +#### 通配符 + +使用 `*` 来匹配路径中存在的文件或者文件夹。说起来抽象,举个例子就明白了。假定当前目录下有这样几个文件: +``` +file1.txt +file2.txt +file3.txt +dir1 +dir1/file.txt +dir2 +dir2/file.txt +dir3 +dir3/file.txt +``` +那么下面这个命令: +```bash +myprogram * +``` +等价于: +```bash +myprogram file1.txt file2.txt file3.txt dir1 dir2 dir3 +``` +下面这个命令: +```bash +myprogram dir* +``` +等价于: +```bash +myprogram dir1 dir2 dir3 +``` + +需要注意的是,`*` 是由 bash 负责解释和展开的,而不是由 `myprogram` 负责解释和展开的; + 或者说,`myprogram` 从来不知道 `*` 的存在,它收到的信息已经是展开后的结果了。 + +另一个常见的通配符是 `~`,它表示当前用户的家目录(也就是你刚登陆时,所在的那个目录[^10])。例如: +```bash +myprogram ~/file.txt +``` +可能等价于 +```bash +myprogram /home/chn/file.txt +``` +这个通配符也是由 bash 负责解释和展开的。 +(等等,这个东西是通配符吗?) + +#### for 循环,大括号展开 + +bash 也支持 `for` 循环,例如: +```bash +for i in 1 2 3; do mkdir $i; cd $i; cd ..; done +``` +这个命令会在当前目录下创建三个文件夹,分别叫 `1`、`2`、`3`,然后依次进入这三个文件夹,再依次返回到当前目录。 +这里用到了 `;` 来分隔多个命令,它的具体用法在新手篇里不会提到,仅在这一个特殊的例子中照葫芦画瓢写就可以了。 + +对于数值,bash 还支持大括号展开,例如: +```bash + + + +[^1]: 这个更底层的程序就是所谓的“内核”。 +[^2]: 无论是 Windows 上的资源管理器还是 Linux 上的这个“黑框框”,它有一个统一的名字,叫做“shell”,也就是“壳”,意思就是它把系统中复杂的细节包装了起来,让用户可以简单地进行删除文件、打开文件夹等操作。 +[^3]: Linux 上最常见的是 bash,学校的超算用的就是这个;次常见的是 zsh,我管理的其它服务器使用的其实就是 zsh 而不是 bash。 + 这两个的基本使用方法是相同的,初学者不需要区分。 +[^4]: WinSCP 默认不会列出隐藏的文件和文件夹,有时会误导新手以为 `.bashrc` 等文件不存在。 +[^5]: 命令原本输出到屏幕上的内容不仅仅来自标准输出,还有标准错误输出(用于输出出错的信息,例如“permission denied”等)等途径。 + 如此使用重定向符号只会重定向标准输出,不会重定向标准错误输出等其它输出。 +[^6]: 实际上是“concatenate”的缩写,但这个单词我,反正我不认识。 +[^7]: 系统在实际复制一个文件夹时,总是需要首先新建文件夹本身,然后新建文件夹里的 +删除文件夹里的内容,而这又需要首先删除文件夹里的文件夹里的内容,以此类推。 + “recursive”就是“递归”的意思。单纯删除空文件夹的参数其实是 `-d`,意为“directory”,但这个参数很少用到,因为 `-r` 也可以删除空文件夹。 +[^8]: 准确说,是 `PATH` 指向的目录。`rm` 等最常用的命令通常位于 `/usr/bin` 或 `/run/current-system/sw/bin`。 +[^9]: 标准输入也不是程序读取用户输入的唯一途径。最常见的例外是一些需要读取密码的程序,为了防止密码泄漏,它们往往会从更底层直接拦截、读取掉。 + 这也是为什么命令行中输入密码时,大多情况下没有任何显示的原因。 +[^10]: 准确说,是 `HOME` 环境变量的值。