0%

重学:Missing Semester

生日攻势期间,由于任务较多,我有一些地方没有认真学习。现在,我需要重新学习一遍Missing Semester。

(未完结)


第一课

当我输入

1
~$ ls -l

输出

1
2
3
4
total 12
-rw-r--r-- 1 palind palind 6 Mar 17 08:43 hello.txt
-rw-r--r-- 1 palind palind 6 Mar 17 08:45 hello2.txt
-rw-r--r-- 1 palind palind 36 Mar 17 09:06 last-modified.txt

ls -l 以长格式(long format)列出目录中的文件和子目录的详细信息。

我的输出解释如下:

第一行: total 12

  • total 12:这一行表示当前目录下(不包括子目录中的文件)所有列出文件所占用的磁盘空间总和。这个数字通常是以“ ”(block)为单位的,具体块的大小可能因系统而异(例如 1K 或 4K)。它是一个估计值,帮助了解这些文件占用了多少存储空间。在这个例子中是 12 个块。

文件/目录信息行 (例如: -rw-r--r-- 1 palind palind 6 Mar 17 08:43 hello.txt)

接下来的每一行都代表一个文件或目录。我们以 hello.txt 这一行为例,把它拆分成几个部分来看:

1
2
-rw-r--r--   1   palind   palind    6   Mar 17 08:43   hello.txt
[1] [2] [3] [4] [5] [6] [7]
  1. [1] 文件类型和权限 (-rw-r--r--)

    • 第一个字符 (-): 表示文件类型。

      • -: 普通文件 (regular file)

      • d: 目录 (directory)

      • l: 符号链接 (symbolic link)

      • c: 字符设备文件 (character device file)

      • b: 块设备文件 (block device file)

      • s: 套接字文件 (socket file)

      • p: 命名管道 (named pipe)

        在这个例子中,所有的条目都是普通文件,所以都是以 - 开头。

    • 后面九个字符 (rw-r--r--): 表示文件的访问权限,分为三组,每组三个字符:

      • 第一组 (rw-)

        : 文件所有者 (Owner/User) 的权限。

        • r: 可读 (read)
        • w: 可写 (write)
        • x: 可执行 (execute)
        • -: 无此权限 所以 rw- 表示所有者可以读写这个文件,但不能执行它。
      • 第二组 (r--)

        : 文件所属组 (Group) 的权限。

        • r-- 表示和这个文件同组的用户只能读取这个文件,不能写入或执行。
      • 第三组 (r--)

        : 其他用户 (Others) 的权限。

        • r-- 表示其他所有用户也只能读取这个文件,不能写入或执行。
  2. [2] 硬链接数 (1)

    • 这个数字表示有多少个硬链接指向这个文件(或者说,有多少个文件名指向磁盘上这份相同的数据)。对于文件来说,通常是 1。对于目录,这个数字至少是 2(目录自身 . 和其父目录中的条目 ..),每增加一个子目录,这个数字会加 1。
  3. [3] 文件所有者 (palind)

    • 这个字段显示拥有此文件的用户的用户名。我是这些文件的所有者。
  4. [4] 文件所属组 (palind)

    • 这个字段显示拥有此文件的用户组的组名。在这里,这些文件属于 palind 用户组。
  5. [5] 文件大小 (636)

    • 这个数字表示文件的大小,单位是字节 (bytes)。
      • hello.txt 的大小是 6 字节。
      • hello2.txt 的大小是 6 字节。
      • last-modified.txt 的大小是 36 字节。
    • 如果这是一个目录,这个大小通常是目录本身元数据的大小,而不是目录内所有文件大小的总和。
  6. [6] 最后修改日期和时间 (Mar 17 08:43)

    • 这个字段显示文件内容最后一次被修改的日期和时间。
      • Mar 17: 月份和日期 (3月17日)
      • 08:43: 时间 (上午8点43分)
    • 如果文件最后修改时间距离当前时间较远(通常是超过6个月),这里可能会显示年份而不是具体时间。
    • 这里显然是我生日攻势那一周的时间。
  7. [7] 文件名/目录名 (hello.txt, hello2.txt, last-modified.txt)

    • 这是文件或目录的名称。

上面的解释中,我用斜体表示我有点陌生的词汇。现在我来补充一下它们的含义,

1. 块 (block) 到底是什么?

“块”是指文件系统分配存储空间的基本单位。

  • 想象一下:硬盘就像一个巨大的仓库,仓库里有很多大小相同的储物格(这些储物格就是“块”)。
  • 存储文件时:当你要存放一个文件(比如一件货物)时,文件系统会给这个文件分配一个或多个储物格。即使文件很小,只占用了储物格的一小部分,整个储物格也会被标记为“已使用”(至少对于那个文件的这部分而言)。
  • 大小:一个块的大小在不同的文件系统或操作系统配置中可能是不同的,例如 512 字节、1KB (1024 字节)、4KB (4096 字节) 等。
  • total 的含义ls -l 输出中的 total 行显示的数字,是该目录下所有列出文件实际占用的块的总数。这并不完全等于所有文件大小(以字节为单位)的总和除以块大小,因为文件系统有一些额外的开销,并且即使是很小的文件也至少会占用一个块。

简单来说,块是文件系统管理磁盘空间的最小单元

2. 特殊文件类型

ls -l 输出的第一个字符代表文件类型,除了常见的 - (普通文件) 和 d (目录) 外,还有一些特殊的:

  • 符号链接 (Symbolic Link / Symlink),用 l 表示
    • 可以把它看作是 Windows 中的“快捷方式”或 macOS 中的“替身”。
    • 它是一个特殊类型的文件,其内容是指向另一个文件或目录的路径
    • 当你操作符号链接时(比如读取它),系统通常会自动将操作重定向到它指向的实际文件或目录。
    • 优点:非常灵活,可以跨文件系统链接,可以链接到目录。
    • 注意:如果原始文件被删除,符号链接就会变成一个“坏链接”或“死链接”,因为它指向的目标不存在了。
  • 字符设备文件 (Character Device File),用 c 表示
    • 代表一种与系统硬件或驱动程序交互的接口,这些设备以字符流(一次一个字节)的方式处理数据,不支持随机访问。
    • 例子:
      • 终端 (/dev/tty, /dev/pts/0):你输入的命令和看到的输出就是通过字符设备。
      • 串口 (/dev/ttyS0)
      • 空设备 (/dev/null):任何写入它的数据都会被丢弃。
      • 随机数生成器 (/dev/random, /dev/urandom)
  • 块设备文件 (Block Device File),用 b 表示
    • 也代表与系统硬件或驱动程序交互的接口,但这些设备以数据块(固定大小的数据块,比如 512 字节或 4KB)的方式处理数据,并且通常支持随机访问(即可以直接读写任意位置的数据块)。
    • 例子:
      • 硬盘驱动器 (/dev/sda, /dev/sdb)
      • SSD 驱动器 (/dev/nvme0n1)
      • U盘
      • CD/DVD驱动器
  • 套接字文件 (Socket File / UNIX Domain Socket),用 s 表示
    • 这是一种特殊的文件,用于在同一台计算机上运行的不同进程之间进行通信 (IPC - Inter-Process Communication)
    • 进程可以通过读写套接字文件来相互发送和接收数据,就像网络通信一样,但它不经过网络协议栈,效率更高,仅限于本地。
    • 很多服务程序(如数据库、Web 服务器)会在本地创建一个套接字文件,以便其他本地客户端程序可以连接到它。
  • 命名管道 (Named Pipe / FIFO),用 p 表示
    • FIFO 的意思是 “First-In, First-Out” (先进先出)。这也是一种用于进程间通信的特殊文件。
    • 它像一个单向管道:一个进程向管道的一端写入数据,另一个进程从管道的另一端读取数据。写入的数据会按照写入的顺序被读取。
    • 与无名管道(通常在shell中用 | 创建)不同,命名管道在文件系统中有一个可见的名字,因此不相关的进程也可以通过这个名字找到并使用它进行通信。

3. “可执行”是什么意思? (x 权限)

“可执行”权限对于文件和目录有不同的含义:

  • 对于文件 (例如,hello.sh 或一个编译后的程序)
    • 如果一个文件有可执行权限,意味着操作系统允许你尝试将这个文件作为程序来运行
    • 对于二进制程序(如C语言编译后的程序),CPU 会直接执行里面的机器码。
    • 对于脚本文件(如 Shell 脚本、Python 脚本),需要解释器(如 bashpython)来读取并执行脚本中的命令。通常脚本文件的第一行会指定解释器 (例如 #!/bin/bash)。
    • 如果你尝试运行一个没有可执行权限的文件,系统会拒绝并提示 “Permission denied”。
  • 对于目录 (例如,一个名为 my_project 的文件夹)
    • 如果一个目录有可执行权限,意味着你可以进入 (access/traverse) 这个目录
    • 例如,你可以使用 cd my_project 命令进入这个目录。
    • 如果你想列出目录的内容 (ls my_project),你通常需要对该目录同时拥有读权限 (r) 和执行权限 (x)。
    • 如果你想在目录中创建或删除文件,你还需要对该目录有写权限 (w)。
    • 如果一个目录没有执行权限,即使你有读权限,你也可能无法列出其内容,或者无法访问其中的文件(除非你知道文件的确切路径并且有权限访问那些文件本身)。

4. 文件所属组 (File Group) 是什么意思?

在 Unix/Linux 系统中,每个用户都属于一个或多个用户组。文件所属组是文件权限管理的一部分:

  • 目的:允许一群用户共享对某些文件的访问权限,而不是将权限赋予单个用户或所有其他用户。
  • 运作方式:
    1. 每个文件都有一个所有者 (owner) 和一个所属组 (group)
    2. ls -l 输出的第三列是所有者,第四列是所属组。
    3. 权限位 (rwxrwxrwx) 中的中间三个字符 (rwx) 就是定义了文件所属组中的成员对该文件拥有的权限。

文件所属组是一种非常有效的方式来组织和管理多用户环境下的文件访问控制。

mv 命令是 “move” 的缩写,它的主要功能是移动文件或目录,或者重命名文件或目录。它非常常用且功能强大。

基本语法

mv 命令的基本语法有两种主要形式:

  1. 移动/重命名单个文件或目录:

    1
    mv [选项] <源文件或目录> <目标文件或目录>

    例如:mv source.txt destination.txt

  2. 移动多个文件或目录到一个目标目录:

    1
    mv [选项] <源文件1> [<源文件2> ...] <目标目录>

    例如:mv file1.txt file2.txt documents/

mv 命令的行为详解

mv 的具体行为取决于 <目标> 参数的类型:

  1. <目标> 是一个已存在的目录名时:

    • <源文件或目录> (可以是单个或多个) 会被移动到 <目标目录> 内部。

    • 文件名保持不变(除非在目标目录中已存在同名文件,行为取决于选项)。

    • 示例:

      1
      2
      3
      4
      5
      6
      7
      8
      # 将 my_file.txt 移动到 existing_folder 目录中
      mv my_file.txt existing_folder/

      # 将 report.docx 和 notes.txt 移动到 backup 目录中
      mv report.docx notes.txt backup/

      # 将 my_stuff 整个目录移动到 archives 目录中
      mv my_stuff/ archives/
  2. <目标> 不是一个已存在的目录名时:

    • 如果 <源文件或目录> 只有一个:

      • 如果

        1
        <目标>

        不存在:

        1
        <源文件或目录>

        会被重命名为

        1
        <目标>
        1
        2
        3
        4
        5
        # 将 old_name.txt 重命名为 new_name.txt
        mv old_name.txt new_name.txt

        # 将 old_directory 重命名为 new_directory
        mv old_directory new_directory
      • 如果

        1
        <目标>

        是一个已存在的文件名:

        1
        <源文件>

        (必须是文件)会覆盖

        1
        <目标文件>

        (行为也取决于选项,如

        1
        -i

        1
        -f
        1
        2
        # 用 source_file.txt 的内容覆盖 existing_file.txt (可能会有提示)
        mv source_file.txt existing_file.txt
    • 如果 <源文件或目录> 有多个: 这种情况下 <目标> 必须是一个目录,否则会报错。

常用选项

mv 命令有很多选项,以下是一些最常用的:

  • -i (--interactive): 交互模式。在覆盖一个已存在的文件前给出提示,并要求用户确认 (输入 yn)。这非常有用,可以防止意外覆盖重要文件。很多系统会将 mv 默认为 mv -i 的别名。

    1
    2
    3
    mv -i important_document.txt archive/
    # 如果 archive/important_document.txt 已存在,会提示:
    # mv: overwrite 'archive/important_document.txt'?
  • -f (--force): 强制模式。如果目标文件已存在,不经提示直接覆盖。与 -i 选项相反。使用此选项时要特别小心,因为它可能导致数据丢失。

    1
    mv -f temp_file.txt final_version.txt
  • -n (--no-clobber): 不覆盖。如果目标文件已存在,则不执行移动/重命名操作,也不会提示。这在脚本中防止意外覆盖时很有用。

    1
    2
    mv -n new_version.txt old_version.txt
    # 如果 old_version.txt 已存在,new_version.txt 不会被移动,也不会有任何提示。
  • -v (--verbose): 详细模式。显示每个文件移动或重命名的详细过程。

    1
    2
    3
    4
    mv -v *.log old_logs/
    # 输出可能像这样:
    # renamed 'app.log' -> 'old_logs/app.log'
    # renamed 'server.log' -> 'old_logs/server.log'
  • -u (--update): 更新模式。只有当 <源文件><目标文件> 更新(修改时间更晚),或者 <目标文件> 不存在时,才会执行移动操作。

    1
    mv -u latest_draft.txt final_report.txt
  • -b (--backup): 为每个被覆盖或删除的目标文件创建一个备份。备份文件名通常是在原文件名后加上一个波浪号 ~。可以与 --suffix=后缀 结合使用自定义备份后缀。

    1
    2
    3
    mv -b config.ini old_configs/
    # 如果 old_configs/config.ini 存在,它会被重命名为 old_configs/config.ini~
    # 然后新的 config.ini 会被移入。

示例汇总

  1. 重命名文件:

    1
    mv oldfile.txt newfile.txt
  2. 移动文件到指定目录:

    1
    mv myfile.txt /home/user/documents/
  3. 移动文件到指定目录并重命名:

    1
    mv myfile.txt /home/user/documents/archived_file.txt
  4. 重命名目录:

    1
    mv old_project_folder/ new_project_folder/
  5. 移动目录到指定位置:

    1
    mv my_photos/ /media/usb_drive/backups/
  6. 移动多个文件到一个目录:

    1
    2
    mv report_v1.doc report_v2.doc final_reports/
    mv *.jpg holiday_pics/ # 使用通配符移动所有 .jpg 文件
  7. 交互式移动,防止覆盖:

    1
    mv -i important_data.csv /shared_folder/
  8. 强制移动:

    1
    mv -f temporary_output.log production.log

mv 是一个基础且强大的命令,熟练掌握它对于日常的命令行操作非常有帮助。如果你有特定的使用场景或疑问,可以再具体提出来!

cp 命令例子如下:

1
2
3
4
5
6
7
8
9
10
~$ ls
foo.txt hello2.txt last-modified.txt
~$ cp foo.txt .
cp: 'foo.txt' and './foo.txt' are the same file
~$ cp foo.txt foo2.txt
~$ ls
foo.txt foo2.txt hello2.txt last-modified.txt
~$ cp foo.txt ./foo3.txt
~$ ls
foo.txt foo2.txt foo3.txt hello2.txt last-modified.txt

rm rmdir 不赘述。

看看命令cat 和流式> < >> 的例子:

1
2
3
4
5
6
7
8
9
10
11
12
~$ echo hello > hello.txt
~$ cat hello.txt
hello
~$ cat < hello.txt
hello
~$ cat < hello.txt > hello2.txt
~$ cat hello2.txt
hello
~$ cat < hello.txt >> hello2.txt
~$ cat hello2.txt
hello
hello

看一下pipe 的例子:

1
~$ ls -l / | tail -n1

会输出ls -l / 的最后一行。

| tail -n1 这部分是一个 管道(pipe)命令,把前一个命令的输出交给 tail 使用。tail -n1 :取输出的最后 1 行

再结合流,

1
2
3
~$ ls -l / | tail -n1 > ls.txt
~$ cat ls.txt
drwx------ 2 root root…………

sudo 命令可以让你以super user的身份运行。

命令:

1
sudo echo 500 > brightness

无法运行的根本原因是:sudo 只提升了 echo 的权限,而不是整个重定向操作 > brightness 的权限。

🔍 问题分析

在 Linux 中,重定向符号 > 是由 shell 处理的,不是 echo 命令的一部分。你这个命令的执行方式相当于:

1
(sudo echo 500) > brightness

这意味着:

  • echo 500 是以 root 权限运行的;
  • > brightness 的重定向是由当前普通用户的 shell 来执行的;
  • 如果 brightness 是一个只允许 root 写入的文件(比如 /sys/class/backlight/.../brightness),你当前用户没有写权限,因此重定向失败。

✅ 正确的写法

要让整个操作以 root 权限执行,有两种常用方法:

方法一:使用 tee 命令

1
echo 500 | sudo tee brightness
  • tee 会以 root 权限写入 brightness 文件;

  • 注意:它默认会把内容输出到终端,如果不想看到输出,可以加上 > /dev/null

    1
    echo 500 | sudo tee brightness > /dev/null

方法二:使用 sudo sh -c

1
sudo sh -c 'echo 500 > brightness'
  • 整个字符串 'echo 500 > brightness' 会由 sh 执行,且在 root 权限下运行。

补充:tee

tee 是 Linux/Unix 系统中的一个命令,用于读取标准输入并将内容同时输出到标准输出和文件中。它的名字来自“T形管道”,象征着“一进多出”。


🧪 简单示例

1
echo "hello" | tee file.txt
  • 作用:将 "hello" 同时:
    • 显示在终端(标准输出);
    • 写入到 file.txt 文件中。

输出结果:

1
hello   ← 显示在终端

文件 file.txt 中内容变为:

1
hello

✅ 常见用法

1. 与 sudo 搭配使用(用于写只读文件)

1
echo 500 | sudo tee /sys/class/backlight/intel_backlight/brightness

如果你不能直接重定向到文件(因为当前用户权限不够),sudo tee 就能帮你实现。

2. 阻止终端输出

1
echo "quiet" | tee file.txt > /dev/null

> /dev/null 表示不在终端打印,只写入文件。

3. 同时写入多个文件(使用 tee -a 追加模式和 tee file1 file2

1
echo "log entry" | tee -a log1.txt log2.txt

📌 参数说明(常用)

参数 含义
-a 以追加方式写入文件,而不是覆盖
-i 忽略中断信号(如 Ctrl+C)

📘 总结

tee 就像一个“分流器”,适合在你需要 同时把输出保存到文件显示在屏幕上 的时候使用,尤其适合和 sudo 搭配写入系统文件。


第二课

1
2
3
~$ foo=bar
~$ echo $foo
bar

foo这个变量存储在当前shell的进程的内存RAM中。

1
2
~$ echo "Value is $foo"
Value is bar

变量名被解析了。

现在,我先

1
2
$ echo "" > mcd.sh
$ vim mcd.sh

然后写入一个函数,并退出vim。检查一下,

1
2
3
4
5
$ cat mcd.sh
mcd () {
mkdir -p "$1"
cd "$1"
}

现在运行,

1
2
3
$ source mcd.sh
$ mcd test
~/learnmissing/test$

1
$ echo "We are in $(pwd)"

$()是命令替换语法。

1
$ cat <(ls) <(ls ..)

<(command)进程替换 的语法。

特性 $(...) (命令替换) <(...) (进程替换)
功能 执行命令,把结果内容当作字符串 插入 执行命令,把结果当作临时文件 使用
返回内容 命令执行结果的文本内容 返回一个伪文件名 (如 /dev/fd/63
用途 拼接字符串、赋值、参数传递等 用于需要“文件”的命令参数,如 catdiff
能否用于重定向? ✅ 可以,输出是纯文本 ❌ 不是文本,而是伪文件路径
示例 echo "Today is $(date)" cat <(ls)

touch 可以创建新文件。

find 是 Linux/Unix 中一个非常强大的命令,用来在目录树中查找符合条件的文件或目录

1
find [路径] [查找条件] [执行动作]
  • 路径:指定从哪个目录开始查找,比如 . 当前目录,/ 根目录
  • 查找条件:比如按文件名、大小、时间、类型等筛选
  • 执行动作(可选):找到后执行某个命令,比如删除、打印等
  1. 按文件名查找

找当前目录及子目录下,所有名字是 file.txt 的文件:

1
find . -name "file.txt"

支持通配符(*):

1
find . -name "*.log"
  1. 按文件类型查找

找当前目录下所有目录:

1
find . -type d

找所有普通文件:

1
find . -type f
  1. 按文件大小查找

找大于 1MB 的文件:

1
find . -size +1M

找小于 100KB 的文件:

1
find . -size -100k
  1. 按修改时间查找

找最近 7 天内修改过的文件:

1
find . -mtime -7

找 10 天前修改过的文件:

1
find . -mtime +10
  1. 查找后执行命令

找到 .log 文件并删除:

1
find . -name "*.log" -exec rm {} \;

找到 .sh 文件并显示详细信息:

1
find . -name "*.sh" -exec ls -l {} \;

总结表格

参数 作用 示例
-name 按文件名查找 find . -name "*.txt"
-type 按类型查找(f=文件,d=目录) find /tmp -type d
-size 按大小查找 find . -size +10M
-mtime 按修改时间查找 find . -mtime -5
-exec 查找后执行命令 find . -name "*.log" -exec rm {} \;

grep 是 Linux 中非常常用的命令,用于搜索文件中符合条件的行,尤其常用于查找包含某个关键词的行。

1
grep [选项] '关键词' 文件名
  1. 在文件中查找包含“apple”的行
1
grep "apple" fruits.txt

输出:显示 fruits.txt 中包含 apple 的所有行。

  1. 忽略大小写搜索
1
grep -i "apple" fruits.txt

能找到 AppleAPPLEaPPle 等各种形式。

  1. 显示行号
1
grep -n "apple" fruits.txt

输出中会加上匹配行的行号,方便定位。

  1. 递归搜索(搜索目录中所有文件)
1
grep -r "main" ./src

会在 ./src 目录及其子目录中查找包含 main 的行。

  1. 搜索不包含关键词的行
1
grep -v "apple" fruits.txt

显示所有不包含 “apple” 的行。

  1. 用正则表达式搜索
1
grep "^a.*e$" fruits.txt

匹配所有以 a 开头、以 e 结尾的行。

grep 常用选项汇总

选项 说明
-i 忽略大小写
-n 显示匹配的行号
-v 反向匹配(显示不包含关键词的行)
-r 递归搜索目录
-l 只列出匹配的文件名
--color 高亮显示匹配的内容

🎯 举个实际例子:找出 Python 文件中包含 def 的行

1
grep "def" *.py

再加上行号:

1
grep -n "def" *.py

可以结合 pipe(管道符 |)使用:

1
cat file.txt | grep "error"

这与

1
grep "error" file.txt

一样。

第三课