This commit is contained in:
2024-11-17 23:56:05 +08:00
parent 54e09b242c
commit af350c1da2

409
content/blog/linux-guide.md Normal file
View File

@@ -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。
* 编程时常用的概念(编译、链接、解释器等)。
在这之后就区分“专业”了,例如做科学计算的话,需要了解:
* 常用的并行计算协议MPIOpenMPCUDAOpenCLSYCL。这里其实我也缺一些知识我需要自己先把自己的疑惑解决了再写。
* 队列系统。但是我已经写过了。
当然也可以写一些其它的。但这些要么已经有人写过了写得很好我受益匪浅,我觉得不需要我来写(例如 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` 环境变量的值。