TheUnknownBlog

Back

Shell Learning

Back to all courses

Shell Learning

6 modules · 14 lessons

Welcome to the Shell training ground!

In today’s world of beautiful graphical interfaces, you might wonder why we still need to learn the command line. The answer is simple: the shell is one of the most powerful tools a developer can master. It allows you to automate tasks, manage files efficiently, and access tools that don’t have graphical interfaces.

This course is designed to bridge the gap in shell usage and help you become a more efficient and confident developer. We’ll cover the fundamentals that will serve you throughout your programming journey, from basic navigation to advanced piping and package management.

We’ll only cover the basics of shell usage - but these basics are enough to cover most of your needs from now through your sophomore year. The shell is a very powerful and complex tool with much more to explore. More importantly, we hope Shell Dojo will help you stay calm when encountering problems in the future, understand how the shell works, and know how to consult documentation and find solutions.

There are 6 modules, click the buttons below or the table of contents to navigate between them.

Intro

Welcome to Shell

Welcome to the Shell learning journey! Before diving into the powerful features of the command line, we'll introduce you to what the shell is and why it matters.

In this module, you will learn the fundamental concepts of the shell, understand why command-line interfaces are still relevant in the modern era, and get familiar with the basic structure of how we'll be learning throughout this course.

在图形界面尚不普及的年代, 人们通过我们称之为 命令行(command line) 的文本界面与计算机交互. 用户输入命令, 这些命令会先由 shell 解释, 然后再转发给底层操作系统.

你可能会好奇: 在 GUI (图形化界面)已经如此普及的今天, 我们为什么还需要学习 shell?

首先, 作为计算机专业的学生, 我们经常需要使用只能通过命令行访问的底层工具. 其次, GUI 往往在批量、自动化任务上不如 CLI 高效, 这就是为什么资深用户更偏爱 CLI(命令行界面)工具的原因.

对许多开发者来说, shell 是一门艺术. 掌握 shell 的使用将显著提升你的效率, 并加深你对计算机系统的理解. 这正是我们开发这门速成课程的原因:

弥合 shell 使用上的差距, 帮助你成为一名更高效更自信的开发者.

在本教程中, 你将学到:

  • 文件的读写及权限管理
  • 命令行的基本操作
  • 管道与相关操作
  • 软件包及其管理

当然, shell 的极限远不止这些,更多的操作空间留给诸位自己探索.

本教程将会以关卡的形式展现,当你学习新的 shell 使用技巧或者 CLI 工具后,你需要运用所学的内容去解决挑战,它可能是打开某个已知的文件,亦或是正确地执行某些命令.最后,你会获得 flag, 它长这个样子:

flag{...}
plaintext

将它复制到浏览器下方的 “标志” 位置以解锁下一关.

有些关卡有全屏的教程 (就像你现在在阅读的这个); 它会在你登录时自动打开. 如果你不小心退出了, 可以通过运行 /challenge/tutorial 命令重新进入.

在这个入门关卡里, 你的 flag 是:

[flag]
plaintext

祝一切顺利, 愿 shell 常伴你左右.

Navigation

Snooping Around

Now that you understand what the shell is, it's time to learn how to navigate through your file system. This module will teach you the fundamental skills of moving around directories and manipulating files and folders from the command line.

You'll learn how to read and explore your file system, how to create and organize directories, and how to create and modify files. These navigation and file manipulation skills form the foundation of all command-line work.

在 Linux 中有一个很基本的观点: 一切皆文件 (Everything is a file). 掌握了文件系统的操作, 在 Shell 中就有了立足之地.

因此大部分教授 Shell 技巧的教程都会从浏览文件系统开始, 本教程也不例外.

作为文件系统的第一节, 本节会介绍 pwd, ls, cd 命令.

pwd: 显示当前工作目录#

每时每刻, 某个 Shell 中的用户都有一个位置. 我们将其称为当前工作目录 (CWD, Current Working Directory), 表示 “你现在在哪里”. 我们可以用 pwd (Print Working Directory) 命令显示它.

在终端输入:

pwd
plaintext

输出: /home/hacker, 这意味着你现在的工作目录是 /home/hacker.

值得一提的是, 这里的路径通常以 /... 的形态出现. 在 Linux 中, / 是一个特殊的目录, 它是所有文件和文件夹的根目录 (Root Directory).

我们从根目录开始, 先进入 home 文件夹, 再进入 hacker 文件夹, 这样就到达了 /home/hacker. 这样的路径被称为绝对路径.

你可以将绝对路径想象成 Windows 资源管理器上方显示的那一串路径. 和 Windows 不同的是, Linux 的路径分隔符是 /, 而不是 \.

ls: 列出目录内容#

命令 ls (List) 可以用来告诉你 “某个目录里都有什么东西”. 它接受一个路径参数, 输出该路径对应文件夹内的内容. 比如, 你如果想要看根目录下的所有文件和文件夹, 你可以输入:

ls /
plaintext

这个路径参数是可选的. 如果不写路径, ls 会默认列出当前工作目录的内容.

ls 这个命令有很多参数, 这里列举几个常用的:

  • -a : 显示所有文件, 包括隐藏文件 (以 . 开头的文件)
  • -l : 以长列表的形式显示文件的详细信息 (权限, 所有者, 大小, 修改时间等)

打开 wsl, 试试运行 ls -al. 你看到了什么?

你会发现, 在所有文件夹里有两个特殊的 “文件” ... 出现在列表中. 它们分别表示当前目录和上一级目录.

例如, 你可以使用 ls .. 来查看上一级目录的内容, 用 ./main 来指示当前目录下的 main 文件.

事实上, 作为路径, 不加任何前缀的 main 很多时候也能指示当前目录的 main 文件. 但是在某些情况下, 比如你想要执行当前目录下的 main 文件时, 你必须要在终端中输入 ./main 才能成功执行. 在大多数 shell 的默认设置中, PATH 不包含当前目录 .; 因此执行当前目录下的可执行文件需要显式写 ./main, 否则会得到 command not found.

cd: 切换目录#

如果我们需要改变自己的当前位置, 我们需要 cd (Change Directory) 命令.

cd 命令接受一个路径参数. 如果该路径存在, 那么你的当前工作目录就会被切换到你输入的路径. 如果你直接输入 cd 而不带任何参数, 那么你的工作目录会被切换到你的主目录 (Home Directory).

主目录通常位于 /home/用户名 下, 代表着某个用户的个人空间. 你可以把它想象成 Windows 系统中的 “C:\Users\用户名”. 一般我们会将所有自己的文件都放在主目录下.

你在终端左侧提示栏中看到的 ~ 符号, 就是主目录的简写. cdcd ~cd /home/用户名 是等价的.

~ $ ls
file-1 folder-1 folder-2

~ $ cd folder-1

~/folder-1 $ pwd
/home/用户名/folder-1

~/folder-1 $ cd ../folder-2

~/folder-2 $ pwd
/home/用户名/folder-2
plaintext

Commands

Commanding Commands

With navigation skills under your belt, it's time to dive deeper into understanding how commands actually work. This module will teach you about command structure, options, arguments, and how to get help when you need it.

You'll learn the anatomy of shell commands, discover how to find and use more advanced commands, and understand the shell environment itself including variables and configuration. This knowledge will make you much more effective at using any command-line tool.

在上一关卡中, 我们逐步试图贯彻的观念是: 文件构成了 linux 的系统的核心. 你学会了如何查看文件系统, 如何在文件系统中执行读写操作; 在本关卡中, 我们将会展示 linux 中最为精华的部分之一: 命令行与指令. 我们希望通过这一关卡, 你能够理解 Shell 中何为指令, 何为环境变量, 并借此掌握 command 的调用以及如何定制你的 Shell 环境.

前排提醒: 这一整个章节可能会略微有点困难😈. 如果遇到问题, 请不要气馁. 如果有你认为没有全部掌握的内容, 阅读后续章节, 增强对于 Shell 的理解, 然后再回过头来看看.

调用指令#

在 Shell 中, 你可以通过输入指令来操作系统, 就像你在上一个章节使用 ls 命令来列出文件和目录一样.

command-name arg1 arg2 ...
plaintext

在上面的语法中, command-name 是你想要执行的指令的名称, 后面的 arg1, arg2 等是传递给该指令的参数.

如果参数中含有空格, 你可以使用引号将其括起来, 例如 "arg with spaces", 或者使用反斜杠 \ 来转义空格 arg\ with\ spaces. 在有特殊字符(如 *, ?, $)时, 引号也能避免被 shell 提前解释.

举一个例子: 在 Shell 中, 你可以使用 echo 指令来打印文本到终端. 例如:

echo "Hello, World!"
plaintext

这条指令会输出 Hello, World! 到终端. 其中, echo 是指令名称, "Hello, World!" 是传递给 echo 指令的参数.

很多指令都有自己的选项, 这些选项通常以 --- 开头, 用于修改指令的行为. 大家已经看到, ls 指令的 -l 选项用于以长格式显示文件和目录的信息:

ls -l
plaintext

有时, 选项可以接纳参数, 例如 head -n 10 用于显示文件的前 10 行.

-h 或者 --help 选项通常用于获取指令的帮助信息. 例如, 你可以运行以下命令来查看 ls 指令的帮助信息:

ls --help
plaintext

面对一个你不熟悉的指令, 你总是可以尝试运行 指令名 --help 来查看该指令的用法和可用选项. 通常, 这会打印出该指令的用法说明, 包括可用的选项和参数.

有些指令会有手册页, 你可以使用 man 指令名 来查看该指令的手册页, 获取更详细的信息. 同样的, 你可以用 man ls 来查看 ls 指令的手册页.

Better yet, 你可以使用更加现代化的 tldr 指令名 来获取该指令的简明用法示例. 这往往比 man 更加易读和实用.

tldr 指令是一个社区驱动的项目, 需要你手动进行安装: sudo apt install tldr && tldr --update. apt 的使用会在后续章节中介绍.

Pipes

A Piper's Dream

One of the most powerful features of the shell is the ability to chain commands together and redirect data streams. This module will teach you how to harness this power to create sophisticated command pipelines.

You'll learn about input and output streams, how to redirect data to and from files, and how to pipe the output of one command into another. These techniques are what make the command line such a powerful tool for automation and data processing.

在这个章节中, 我们会简单接触 Shell 中最有用的一项技术: 管道 (pipe) 技术.

而在学习管道之前, 我们需要了解一些常用的命令, 让我们可以做简单的文件/输入输出转发和文本处理.

cat#

之前我们讲到了可以使用文本编辑器打开一个文件, 并查看它的内容. 然而呢, 如果仅仅只是要看一眼文件的内容, 其实不必大动干戈, 而且有很多命令可以帮我们完成这件事情.

打印文件内容, 无非就是将文件输出到终端上; 最常用的命令是 cat.

cat 不是猫, cat 是 “concatenate files and print on the standard output” 的缩写, 中文翻译“将文件拼接并输出到标准输出”; 不过我们暂时可以把它理解成“把文件内容打印到屏幕上”.

你可以这么使用:

# 查看单个文件内容
cat file1.txt

# 拼接文件内容
cat file1.txt file2.txt > combined.txt
plaintext

grep#

当文件较短的时候, 直接用 cat 就能看清楚文件内容了. 但是如果文件很长, 比如系统日志, 你可能就需要一些更高级的工具来帮你分页、过滤和查找你想要的信息.

查看超长文件时, 建议先用 less file 分页查看, 或者 head/tail 抽样; 直接 cat 可能会刷屏. 我们不要求你掌握他们, 留个印象知道他们的存在即可.

我们可以使用 nano 这种文本编辑器来打开文件, 然后用搜索功能查找关键词, 但是这种办法是交互式的, 也就是说没有办法“自动化”. 所以我们就需要一个可脚本化的关键词查询命令, 也就是 grep.

grep 的名字来源于上古时期的文本编辑器 ed 中的命令 g/re/p (globally search a regular expression and print). 所以它的工作就是查找匹配所有满足条件的行并把它们打印出来. 语法是先加待搜索的单词, 再跟上文件名, 比如:

$ cat file.txt
Hello World!
Hello grep!
Hello cat!
$ grep "grep" file.txt
Hello grep!
bash

grep 还有一些常用的选项, 比如:

# 查找文件中包含 "error" 的行
grep "error" logfile.txt

# 忽略大小写匹配
grep -i "error" logfile.txt

# 显示匹配的次数
grep -c "error" logfile.txt
plaintext

其实 cat, grep 本身与重定向没有太大的关系; 但是它的模式匹配与筛选作用是无可替代的. 等我们学习了重定向和管道之后, 它们的作用就能发挥得淋漓尽致了.

在字符串搜索上, Shell 有个更加强大的工具 awksed, 但是使用方式比较复杂. 日常使用不妨使用 python (

Permissions

Perceiving Permissions

Understanding file permissions is crucial for system security and proper file management. This module will teach you about users, groups, and the permission system that controls who can read, write, and execute files.

You'll learn how to read permission strings, how to change file permissions, and understand the concepts of file ownership. This knowledge is essential for managing your own files and collaborating with others on shared systems.

Linux 是一个多用户系统, 这意味着它内置了一套规则来防止文件被不该访问或修改的人读取或改动. 本课将帮助你理解并控制这些规则.

用户和用户组#

在 Linux 中, 一切都归属于某个用户. 每个文件、每个进程(正在运行的程序)都关联到一个特定的用户账户, 系统据此“记账”.

用户 (user): 一个独立的账户. 当你登录时, 你是以特定用户的身份行动. 最强大的用户称为 root(或超级用户), 它对整个系统拥有几乎不受限制的访问能力.

用户组 (group): 用户的集合. 用户组使得同时管理多个用户的权限变得容易. 一个常见例子是用于网页服务的 www-data 组, 你不希望每个普通用户都能改网站文件; 同样, 也不希望 Nginx、Caddy 等服务进程访问你的私人文件. 这篇文章有更详细的说明.

你可以使用 whoami 命令查看你是谁.

whoami
bash

rwx 权限#

在之前的课程中, 你已经学习了 ls -la 命令, 其中 -l 参数显示文件的详细信息, 包括它们的权限. 你可能看到像这样的内容:

total 96
drwxr-x--- 119 theunknownthing  staff   3.7K Sep 15 16:09 ..
drwxr-xr-x   8 theunknownthing  staff   256B Sep 15 16:00 contents
drwxr-xr-x  15 theunknownthing  staff   480B Sep 15 15:59 .git
-rw-r--r--   1 theunknownthing  staff   1.1K Sep  9 20:58 README.md
...
bash

看到 drwxr-xr-x 部分了吗?这就是显示权限的地方.

让我首先解释这些字符的含义. 有三种基本权限:

  • 读取 (r): 查看文件内容或列出目录内容的能力.

  • 写入 (w): 更改或删除文件, 或在目录内创建/删除文件的能力.

  • 执行 (x): 运行文件(如果它是程序或脚本)或进入目录(cd 进入)的能力.

第一个字符表示文件类型: d 表示目录, - 表示普通文件, l 表示符号链接, 以及其他字符表示特殊文件类型.

所以, 在 drwxr-xr-x 15 theunknownthing staff 480B Sep 15 15:59 .git 中:

  • d 表示这是一个目录.

  • 然后你可以看到恰好 9 个字符, 分为三组, 每组三个.

    前 3 个字符 (rwx) 是所有者的权限. 这里, rwx 意味着所有者可以读取、写入和执行. 此文件的所有者是 theunknownthing.

    中间 3 个字符 (r-x) 是用户组的权限. 这里, r-x 意味着用户组可以读取和执行, 但不能写入. 此文件的用户组是 staff.

    最后 3 个字符 (r-x) 是其他人(其余所有人)的权限. 同样, r-x 意味着他们可以读取和执行, 但不能写入.

为了加深理解, 让我们看另一个例子: -rw-r--r-- 1 theunknownthing staff 1.1K Sep 9 20:58 README.md

在看下一页的答案之前, 请先自己解读一下. 思考:

  • 这是什么类型的文件?
  • 所有者拥有什么权限?为什么所有者没有执行权限?
  • 用户组拥有什么权限?
  • 其他人拥有什么权限?

第一个字符是 -, 表示这是一个普通文件.

所有者拥有 rw- 权限, 意味着他们可以读取和写入文件, 但不能执行它(这很明显, 因为它是一个Markdown文本, 你不能运行它).

Tips: 并非所有文本文件都“不能执行”, 而是“默认没有执行位”. 若它是脚本并设置了执行位且有正确的 shebang(例如 #!/usr/bin/env bash), 可直接执行; 即使没有执行位, 也可用解释器显式运行, 例如 bash script.sh 需要脚本具备读权限. 了解 shebang, 请参阅这里. 不过, 我不要求你现在掌握它.

用户组拥有 r-- 权限, 意味着 staff 组中的用户可以查看文件内容, 但不能修改或执行它.

其他人也拥有 r-- 权限, 意味着他们也只能读取文件.

使用 chmod 更改权限#

chmod(change mode)命令用于更改文件的权限. 你可以通过两种常见方式来做这件事: 符号表示法八进制表示法.

我们首先学习符号表示法, 这种方式更直观.

这种方法使用字母(u 表示 user(所有者), g 表示 group, o 表示 others, a 表示 all)和符号(+ 添加, - 删除, = 设置)来修改权限.

# 为owner添加执行权限
chmod u+x script.sh

# 设置others的权限为只读
chmod o=r README.md

# 移除除owner外所有人的读取权限, 这里 `go` 表示 `g` 和 `o`
chmod go-r README.md

# 移除groups的所有权限
chmod g= script.sh
bash

八进制表示法#

这种方法使用数字来表示每个类别的权限. 这有点像二进制! r = 4, w = 2, x = 1. 你可以将想要的权限对应的数字相加.

数字权限含义
7rwx读、写和执行, 4+2+1
6rw-读和写, 2+4
5r-x读和执行, 4+1
4r—只读, 4
0---无权限, 0

使用八进制表示法的 chmod 命令使用一个 3 位数字, 分别代表用户、组和其他人的权限(按此顺序). 要设置权限为 rwxr-xr-x(用户可以做任何事; 组和其他人可以读取和执行), 你将使用数字 755.

chmod 755 script.sh
bash

这将为所有者设置 7(rwx), 为组设置 5(r-x), 为其他人设置 5(r-x).

使用 sudo 获取超级用户权限#

当你需要执行一些你的普通用户账户无权执行的操作时, 例如安装软件或编辑系统配置文件, 会发生什么?

apt update
bash

你会得到错误:

Reading package lists... Done
E: Could not open lock file /var/lib/apt/lists/lock - open (13: Permission denied)
E: Unable to lock directory /var/lib/apt/lists/
W: Problem unlinking the file /var/cache/apt/pkgcache.bin - RemoveCaches (13: Permission denied)
W: Problem unlinking the file /var/cache/apt/srcpkgcache.bin - RemoveCaches (13: Permission denied)
bash

为此, 你需要管理员权限. sudo 命令让你以超级用户(root)身份执行单个命令.

sudo apt update
bash

🈲 root 用户可以做任何事, 包括意外删除你的整个系统. 在使用 sudo 运行任何命令前请仔细检查. 你可能见过一些梗图让你执行 sudo rm -rf /*. 别做! 我不会来帮你恢复系统的!

如果你想亲自登录 root 用户, 可以使用 su 命令(substitute user, 替换用户).

sudo su
bash

这相当于用 sudo 以提权方式运行 su; 在不带参数时, su 默认切到 root.

Tips: 直接运行 su(不带 sudo)默认会询问“目标用户的密码”(通常是 root 的密码). 在 Ubuntu 上, root 账户默认可能被锁定, 此时 su 不可用, 但 sudo su 会要求你输入当前用户的密码(前提是你在 sudoers 中).

当然, su 不仅限于切换到 root. 如果你知道其密码, 你可以切换到任何用户.

su some_username
bash

如果你知道他们的密码, 或者如果你当前的用户拥有比该用户更高的权限, 这将切换到 some_username. 所以从 root 切换到任何用户不需要该用户的密码.

Packages

Super Cow Powers

The final piece of essential shell knowledge is understanding how to install and manage software packages. This module will introduce you to package managers and how they help you keep your system up to date.

You'll learn how to search for, install, and manage software packages using command-line package managers. You'll also learn about package repositories and mirrors, which can help you get faster and more reliable access to software.

到目前为止, 你已经学会了使用 shell 的主要功能.

你知道在 shell 下如何应用各种不同的软件与包, 但是这些包又从何而来?

于是乎在本关卡中, 我们将会接触 aptmirror 的概念.

什么是包#

在计算机科学中, 你可以把包 (Package) 理解为一个或者多个软件的集合, 以及它们需要依赖什么系统资源的信息.

在 Linux 系统中, 安装软件的方式通常是安装包.

包管理器#

一般来说, 所有的包都被由称作包管理器的系统所管理.

以常见的 Ubuntu 系统为例, 其默认包管理器为 apt , 它极大地简化了软件的获取、安装、升级和移除过程:

你往往只需要一条简单命令如 sudo apt install firefox 即可完成软件安装.

不仅如此, 包管理器能够自动处理软件包之间复杂的依赖关系 (Dependencies), 确保所需的所有库和组件都能被正确安装.

如果你想知道为什么这个章节叫做 Super Cow Powers, 那是因为 apt 的一个隐藏彩蛋. 你可以试试 apt --help 读一读末尾, 或者运行 apt moo.

在你安装 wsl2 的早期, 你想必已经尝试了一些命令, 比如:

sudo apt install package    # 安装包
sudo apt update             # 更新包列表
sudo apt upgrade            # 升级已安装的包
plaintext

这些命令通常可以帮助你自动从对应软件包的仓库中下载. 这比在网上找安装包更加方便, 不是吗?

当然, 如果你从网站上下载了一个 .deb 文件, 你也可以使用 apt 来安装它.

sudo apt install ./path/to/package.deb
plaintext