发布于 ,更新于 

NOI Linux 指北

从终端开始

什么是终端?

对于一个计算机来说,输入输出设备显然不是必需品(脱离使用的范畴,没有输入输出设备它也能通电成为一个吉祥物),而最初人们为了与计算机互动,发明了终端,即人类用户与计算机交互的设备。

对于一个计算机操作系统,其运行过程中本身并没有显示任何东西,所谓桌面环境之类在显示器上显示的东西都是后话,最简单的输入输出方式即“我输入文字,计算机输出文字”,于是引入今天我们所要遇到的第一个概念,我称其为“文字终端”,即你输入文字,计算机输出文字的地方。

对于现在来说,以往奢侈的图形显示现在已经满地都是,但是为了还能用上简单快捷的文字终端的功能,出现了“终端模拟器”,实际上就是开一个小黑窗口,里面能够输入输出(类比 CMD)。

打开终端

在桌面或者文件管理器中右键,点击“在终端中打开”

于是出现了一个如上的窗口,这就是一个终端模拟器,里面运行一个被称为“Shell”的进程,这里不多作介绍,理解它能够输入输出文字就行。

什么是 SHELL?

呃,点开这里就默认你已经知道编写程序、输入输出什么的基本概念了。

SHELL,顾名思义,是一层“壳”。前面说到,我们需要一个“终端”来进行交互,而SHELL实现的东西就是“输入”,处理,然后“输出”。相当于给机器套了一层“可交互”的“外壳”。

和终端模拟器的区别?你显然输入要有来源,输出要有目标。终端模拟器就相当于一个屏幕,他能把你键盘里输入的东西扔到SHELL 里面,然后把 SHELL 输出的东西扔到你的眼前。

NOI Linux,或者说 Ubuntu,默认使用的 Shell 是 Bash。

在这里你可以输入命令,做到创建文件,修改文件,删除文件等基本上你在图形界面下能做的所有事情。

先...管理文件?

在 Shell 中,. 表示当前目录,.. 表示当前目录的父目录,/ 表示根目录

Linux 下只有一个根目录,不会像 Windows 一样不同的盘有不同的根目录

也就是如下

Windows:

1
2
3
4
5
6
7
8
9
C:-
|- Windows
|- Program Files
|_ Users

D:-
|- Software
|- Documents
|_ Steam

Linux:

1
2
3
4
5
6
7
/ -
|- usr
|- bin
|- home
|- dev
|- etc
|.....

绝对路径:能唯一确定的路径,也就是说从根目录表示的确定的位置,比如 /usr/local/bin

相对路径:相对于某一目录的位置,比如当前目录是 /usr/local/, 则上面的路径可以表示为 bin./bin

先提供相应的命令:

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
32
33
34
35
36
37
38
39
40
41
42
# 这是一条注释,SHELL 中使用井号做单行注释

# 创建文件夹
mkdir folder_name
# 创建一个 modmodwhr 文件夹
mkdir modmodwhr


# 创建空文件
touch filename
# 创建一个名为 whrakioi 的空文件
touch whrakioi


# 进入一个目录
cd folder_name
# 如果当前目录下有一个 hhyakioi 文件夹,则可以通过这个命令进入该文件夹
cd hhyakioi
# 或者 . 代表当前目录,./hhyakioi 相当于进入当前目录下的 hhyakioi 文件夹
cd ./hhyakioi
# 进入上一级文件夹
cd ..
# 进入根目录
cd /
# 进入某绝对路径
cd /usr/share
# 最后一个杠加不加无所谓
cd /usr/share/

# 复制一个文件到另一个位置
cp /path/to/file /path/to/direction
# 把 1.cpp 复制到 gjxakioi 文件夹
cp 1.cpp gjxakioi/1.cpp
# 复制文件夹需要添加 -r 参数
cp folder_name gjxakioi/folder_name -r

# 移动一个文件到另一个位置,同时可以当重命名用
mv path1 path2
# 重命名 1.cpp 为 2.cpp
mv 1.cpp 2.cpp
# 移动 2.cpp 到 gjxakioi 文件夹
mv 1.cpp gjxakioi/1.cpp

NOI Linux 2 的比赛环境下默认给你挂载 noip 文件夹并在 Linux 桌面上创建快捷方式,因此如果你在桌面上进入终端,则可以通过下面的命令进入 noip 文件夹

1
cd noip

或者

1
cd ./noip

开始写代码吧!

想编辑下文件?

如果你在 Windows 下写好了代码想要粘贴到 Linux 的文件里,则可以

1
2
3
4
# 新建 a.cpp 文件
touch a.cpp
# 使用 nano 编辑器编辑 a.cpp 文件
nano a.cpp

nano 是 GNU 的一款简易终端文本编辑器,相对于 Vi/Vim 来说更适合新手使用

复制了 Windows 的代码之后,确保 VMware 的共享剪贴板已经打开,然后按下 Ctrl + Shift + V 粘贴,然后按下 Ctrl + S 保存,Ctrl + X 退出。

当然,有图形化编辑器供你使用

1
2
# 使用闭源 Visual Studio Code 编辑 a.cpp
code a.cpp

在此之后你可以通过 cat 命令1验证文件内容

1
cat a.cpp

编译 C++ 源码

GNU 编译套件

GNU Compile Collection 是 GNU 开发的一套编译器,在这里我们只使用 GNU C/C++ 编译器部分。

另:GNU计划的目标是开发出一款完全自由的操作系统和软件生态,自由软件运动致力于推进全部软件的隐私保护以及自由化,自由软件属于全人类,自由软件开发者们留下的作品与学习资料是不朽的!如果你对自由软件运动有疑问或有兴趣,可前往GNU 官方网站 了解更多相关内容。

开始编译

1
g++ grainrainisgod.cpp

这个命令会在当前目录下编译 grainrainisgod.cpp 文件,然后默认输出一个 a.out 可执行文件2

可是我懒得重命名文件,所以如何执行输出可执行文件的位置和名称呢?只需要加 -o /path/to/direction 即可

1
g++ grainrainisgod.cpp -o grainrainisgod

当然,你也可以加入其他比赛要求的编译选项,比如说吸个氧(加 -O2 参数)

1
g++ grainrainisgod.cpp -o grainrainisgod -O2

开启编译警告

编译警告在编译的编译阶段和警告阶段发出,能帮你发现一些低级问题和简单 UB,比如著名 UB(但是萌妹 OPTIM 还是犯了这个问题)

1
a = a++ + ++a;

编译器会发出警告

1
2
warning: multiple unsequenced modifications to 'a' [-Wunsequenced]
cout << a++ + ++a << endl;

这样的警告对于比赛选手来说肯定是越多越好越全越好,所以需要开启全部警告,即添加 -Wall 参数

1
g++ grainrainisgod.cpp -o grainrainisgod -O2 -Wall

检查内存错误以及未定义行为

Sanitizer 是谷歌开发的一个检测程序问题的开源套件,一般集成在 gcc 以及 clang 编译器中。相应的,MinGW-W64 并没有为 Sanitizer 做 Windows 兼容,因而无法在比赛环境 Windows 使用(LLVM/Clang 编译器包含的 Sanitizer 支持 Windows,MSVC 的也支持但是只有 AddressSanitizer)(但是显然比赛环境没有这两个编译器),所以显得 Linux 环境非常重要。

Address Sanitizer 用于检测程序的内存错误,比如爆栈等问题。使用方法是添加 -fsanitize=address 选项

Undefined Behaviour Sanitizer 用于检测程序的未定义行为,比如访问空指针,数组越界访问等问题,使用方法是添加 -fsanitize=undefined 选项。

值得注意的是,这两个 Sanitizer 会显著拖慢程序运行速度,所以添加这两个编译选项编译出来的程序在运行时间和内存占用上参考意义不大。同时如果动态分配内存过多可能 Address Sanitizer 会抽风,UBSanitizer 倒是问题不大。所以酌情使用。

总结:

1
g++ grainrainisgod.cpp -o grainrainisgod -O2 -Wall -fsanitize=address -fsanitize=undefined

如上是我赛时经常使用的编译命令。

执行可执行文件

1
./grainrainisgod

. (当前目录)+ / (目录分隔符)+ grainrainisgod 可执行文件名称

注意,这里不能直接输入文件名,必须加上表示路径的符号(相对和绝对都行,此举是为了保证你知道你要执行的程序是“当前目录下”的“这个程序”,别执行成“李鬼”了)。

然后就可以快乐地输入了。

注意:在这个终端中复制使用 Ctrl+Shift+ C,粘贴使用 Ctrl+Shift+V

Ctrl + C 用于终止当前正在运行的程序,也可以在输入命令打错字时 Ctrl + C 重开一行。

进阶:一些 bash 指令

刚才已经学过一些命令了,接下来将会教给大家更多的 bash 相关的东西。

关系运算符

  • command1 && command2:当 command1 执行成功后才会执行 command2
  • command1 || command2:当 command1 执行失败时才会执行 command2

然后给几个练习题:

前置知识

命令echo:后面接一个字符串,作用是输出这个字符串。
示例:echo tibrellalaji,回车,终端出现了tibrellalaji 一串字符。

猜测以下的命令输出结果是什么?(一行一题)

1
2
3
4
echo 1 && echo 23
echo 12 || echo 3
echhhooo 1234|| echo 3
woquzheshishenmezhiling && echo 123
答案
1
2
3
4
5
# 忽略报错信息
1
23
12
3

变量

赋值

1
a=1

bash 变量无需声明,可以直接赋值之后使用。

变量名要求和 C++ 差不多:

  • 字母、数字、下划线
  • 数字不能开头

但是赋值和大部分语言是不同的:等号两边不能有空格。

你不必在乎 bash 的变量类型。

使用

变量名前加 $,两边可以加大括号也可以不加。

示例:

1
2
3
4
5
mainpage=www.tibrella.space

echo ${mainpage}
echo $mainpage
echo https://${mainpage}isgood

容易发现大括号的作用:区分变量名的边界。如果你写的是 echo https://$mainpageisgood 的话后面就是空的了,因为 bash 把 mainpageisgood 识别成了一个变量名,而这个变量不存在(也就是为空)。

输入输出重定向与管道符

这里是重点,在 OI 中写对拍脚本啥的非常能用得上这个东西。

假设有一个文件叫做 testfile,一个程序叫 test,则:

还记得吗?执行文件时必须加 ./,否则不能执行。

  • testfile < ./test:将 test 的输入(stdin)重定向到 testfile,相当于在你的程序中加入了 freopen("testfile","r",stdin)
  • ./test > testfile:将 test 的输出(stdin)重定向到 testfile,相当于在你的程序中加入了 freopen("testfile","w",stdin)。换句话说,test 程序输出的东西将会写入到 testfile 中。
  • 上面两个可以同时使用,比如 joker.in < ./joker > joker.out,相当于程序中加了两行 freopen

然后是管道符,它代表把前一个程序的输出重定向到后一个程序的输入里面。

  • cat testfile | ./test:输出 filename 文件的内容,并把其重定向到 test 程序的输入里面。等价于 testfile < ./test

为啥他叫管道符呢?现在我们写一个输入一个字符串再输出它的程序:

1
2
3
4
5
6
7
8
9
#include <string>
#include <iostream>

int main() {
std::string s;
std::cin >> s;
std::cout << s;
return 0;
}

编译一下:g++ test.cxx -o test

然后执行这个命令:

1
echo pipe | ./test | ./test | ./test

我们发现最后的输出是 pipe
显然,这玩意就像管道,能把不同的机器(程序)的输入输出连接起来。

编写一个 bash 脚本

1
2
touch test.sh # 创建一个 bash 脚本
nano test.sh # 编辑这个 bash 脚本

文件内容:

1
2
#!/bin/bash
echo Hello World

#!/bin/bash 表明你这个 test.sh 文件需要使用 /bin/bash 程序运行。(写 #!/bin/sh 也行,这里讲的语法和 sh/zsh 是兼容的,但是你亲爱的 NOI Linux 只有 bashsh

按下 Ctrl + S 保存,Ctrl + X 退出。

然后你需要让这个文件可以被执行,即给他“被执行”的权限:

1
chmod +x test.sh

执行可以直接当可执行文件执行。

1
./test.sh

输出:

1
Hello World

  1. cat filename 即输出 filename 文件的内容↩︎

  2. Linux 下文件类型的判断与 Windows/DOS 略有不同。众所周知,Windows 下操作系统通过扩展名判断文件类型,也就是说无论你把什么文件改成 .docx 扩展名,Windows 都会用 Word 打开它。但是 Linux 一般通过文件头判断文件类型,也就是说无论你把可执行文件命名成 a.out 还是 a.docx 还是 a.jpg 甚至没有扩展名,输入 .\a.jpg 或相应命令之后终端都会把它当作可执行文件执行。↩︎