Linux基础指令:从Shell原理到权限

你打开终端,敲下 ls,回车。屏幕上跳出一串文件名。

这一来一回花掉的时间不到一秒,但中间至少穿过了三层东西:Shell 把你的输入拆成命令和参数,内核替你执行真正的系统调用,驱动再去问磁盘要数据。你看到的每一个字符,都是这条链路往返一趟的结果。

命令行难以上手,往往是因为中间这层结构不可见。下面把 Linux 最基础的那批指令过一遍,同时说明它们为什么是这样设计的;从系统本身的由来开始,一直到讲到权限模型。

1. Linux 的由来

要讲 Linux,得先讲 UNIX。Linux 的大量设计都继承自它:一切皆文件、目录树、用一堆小工具拼出复杂功能。知道这些从哪儿来,后面的行为才讲得通。

1.1 从 Multics 到 UNIX

1968 年,通用电气、贝尔实验室和麻省理工学院的研究人员合作开发了一个叫 Multics 的操作系统。它在多任务文件管理和用户连接上综合了很多新概念,但工程上过于庞大。

1969 到 1970 年,AT&T 贝尔实验室的 Ken Thompson 和 Dennis Ritchie 在借鉴 Multics 诸多特点的基础上,开发出了 UNIX。它跑在小型机上,满足了科研环境对系统的要求。从一开始,UNIX 就是一个高效的、多用户多任务的操作系统。

1972 年,贝尔实验室开始发放商业版本。使用者之一是加州大学伯克莱分校的计算机系,伯克莱给系统加了许多新特性,这些特性后来成了标准。1975 年,伯克莱由其下属部门 BSD 发行了自己的 UNIX 版本,成为 AT&T 版本的主要竞争者。

1.2 分裂与标准化

版本一多,麻烦就来了:软件开发商不知道自己的程序该跑在哪个版本上合适。到 80 年代中期,出现了两个互相竞争的标准------AT&T 的 System V 和 BSD。

AT&T 为此成立了 UNIX 系统实验室,专门综合不同版本、集中开发标准系统。1991 年,该实验室综合 System V Release 3、SUN OS 和 Xenix 的特点,发行了 System V Release 4。IBM 和惠普等公司则组成了 Open Software Foundation(OSF)与之抗衡,于是市场上并存两个标准商业版本。

1993 年,AT&T 把 UNIX 转卖给 Novell,UNIX 系统实验室成为 Novell 的一部分。SUN 公司把 System V Release 4 融进了自己的 SUN OS,发行了 Solaris。原本互相竞争的两种图形界面(Motif 和 Openlook)也合并成了通用平台环境(CDE)。

1.3 Linux 的出现

1991 年 10 月 5 日,赫尔辛基大学的研究生 Linus Benedict Torvalds 在一个 Usenet 新闻组里宣布,他写出了一个类 UNIX 的小操作系统,取名 Linux。它受到另一个小型 UNIX 系统 Minix 的启发,而 Minix 出自 Andrew S. Tanenbaum 之手。

有个细节常被记错:Torvalds 当天发布的并不是 0.01 版,而是 0.02 版。真正的 0.01 从未公开发布过,因为它不实用,Torvalds 只在第一个 FTP 站点上提供过它的源代码。0.02 版已经能跑 GNU bash 和 gcc,以及为数不多的其他语言。

此后版本从 0.02 到 0.03,又跳到 0.10,随着世界各地程序员陆续加入,很快到了 0.95。正式的 1.0 版本在 1994 年公布。

1.4 严格来说,Linux 只是内核

这里要分清一件事:Linux 本身只是操作系统内核。加上用户空间的应用程序之后,才构成一个完整的 Linux 操作系统。

这正好解释了准确的说法:讲内核的时候说「Linux 内核」,讲完整系统的时候才说「Linux」。

GNU 通用公共许可协议(GNU General Public License,GPL)在这里起了关键作用。它最初由 Richard Stallman 为 GNU 计划撰写,赋予了计算机程序「自由软件」的定义:任何基于 GPL 软件开发衍生的产品,发布时必须采用 GPL 许可证,且必须公开源代码。

正因如此,任何个人和机构都可以自由使用、修改和再发布 Linux 的底层源代码。Linux 也由此成为开源软件最有名的代表。

1.5 发行版:内核之外的整套打包

既然内核加应用才是完整系统,那么「谁来打包」就成了分歧点。常见的发行版大致这样分:

发行版 血统与定位
Debian 极其稳定,适合服务器;对新手不算友好,更适合系统管理员和高级用户
Ubuntu Debian 的衍生版,最受欢迎的免费系统之一;新手友好,服务器和云计算上都常见
RHEL 第一款面向商业市场的发行版,支持 x86 与 x86_64 等多种架构
CentOS 用 RHEL 的免费源代码重新构建,去掉了商标;用 YUM 管理软件包
Fedora 红帽的测试平台,产品成为企业级发行版之前在这里开发测试
Kali Linux Debian 衍生版,面向渗透测试,随带大量现成安全工具,用 APT 管理软件包

今天 Linux 的势力范围早已不限于服务器。移动端的 Android 就构建在 Linux 内核之上;路由器、电视、电子游戏机里也大量运行着定制版 Linux。

2. Shell:用户与内核之间的翻译层

严格说,Linux 指的是那个叫「内核(kernel)」的核心。但普通用户不能直接操作内核,中间隔着一层「外壳」程序,也就是 Shell。

Shell 最简单的定义是命令行解释器(command interpreter),它只做两件事:

  • 把用户的命令翻译给内核处理
  • 把内核的处理结果翻译回给用户

参照 Windows 更容易理解。操作 Windows 时同样不直接接触内核,而是通过图形界面:双击盘符、点开一个程序。Shell 在 Linux 中占据的就是图形界面那个位置,区别在于它接收的是文字,不是鼠标点击。

桌面界面如此直观,实际工作中却几乎都在使用命令行,原因在于图形界面把每一步操作都固定成了一个人工动作,而命令行可以写进脚本、可以互相拼接、可以顺着网络传输。一行 ps aux | grep nginx 表达的意思,点开若干个窗口也未必凑得出来,凑出来了也没法自动重复。远程运维时的区别更明显:SSH 传的是一行文本,不是像素。

2.1 远程登录

通常的开发方式是用终端软件连到远程 Linux 主机。流程就三步:

bash 复制代码
# 1. 在 Linux 主机上查自己的 IP
ifconfig

# 2. 在本地终端里连过去(ip 换成上一步看到的结果)
ssh 192.168.1.100

# 3. 提示输入用户名密码,输入即可登录

2.2 命令的通用形式

几乎所有 Linux 命令都是这个形式:

复制代码
命令 [选项] [参数]

选项以 - 开头,可以合并写(ls -a -l 等价于 ls -al),参数则是命令作用的对象。这个结构不必刻意记忆,但掌握了它,查阅 man 手册时就知道该往哪里找。

另外三个热键值得提前熟悉,可以减少大量重复输入:

按键 作用
Tab 命令补全和文件名补全。按一下没反应就按两下,会列出所有候选
Ctrl+C 让当前正在跑的程序停下来
Ctrl+D 表示键盘输入结束(EOF),也可以用来退出当前 Shell

3. 路径:为什么每个文件都有唯一的名字

Linux 把磁盘上的文件和目录组织成一棵目录树,根是 /。这棵树有两个特点:

  • 普通文件一定是叶子节点
  • 目录可能是叶子(空目录),也可能是中间的路径节点

还有一条更重要的性质:任何一个节点都只有一个父节点 。从根出发定位某个文件,路径因此是唯一的。树状组织存在的理由就在这里:要能快速、唯一地定位到指定文件。

由此引出两种写法:

  • 绝对路径 :从 / 开始,不依赖当前所在位置
  • 相对路径:相对于当前所处目录来定位

两者的适用场景不同。绝对路径不会因为用户位置的改变而丧失唯一性,所以配置文件里经常用绝对路径 ;相对路径写起来短,命令行里用得多

写路径时有两个符号需要记住:. 表示当前目录,.. 表示上一级目录。~ 则代表当前用户的家目录。

4. 定位与移动:pwd、ls、cd

4.1 pwd:我在哪

bash 复制代码
$ pwd
/home/jys/test

功能只有一个,但它是所有破坏性操作前成本最低的检查。执行 rmmv 之前先运行 pwd,可以避免相当一部分误操作。

4.2 ls:观察目录

bash 复制代码
ls                 # 列出当前目录
ls -a              # 包括以 . 开头的隐藏文件
ls -l              # 长格式:权限、链接数、所有者、组、大小、时间
ls -d              # 把目录本身当文件显示,而不是列出它的内容
ls -i              # 显示 inode 索引号
ls -F              # 在文件名后加类型标记:* 可执行,/ 目录,@ 符号链接
ls -r              # 反向排序
ls -t              # 按修改时间排序
ls -R              # 递归列出所有子目录
ls -1              # 一行只输出一个文件

-F 会加上类型标记,可以直接看出条目类型:

标记 含义
* 可执行的普通文件
/ 目录
@ 符号链接
` `
= 套接字(socket)

-a-l 组合起来是最常用的:

bash 复制代码
$ ls -al
total 12
drwxrwxr-x  3 jys jys 4096 Jan 11 15:01 .
drwx------ 22 jys jys 4096 Jan 11 14:21 ..
drwxrwxr-x  2 jys jys 4096 Jan 11 14:22 dir
-rw-rw-r--  1 jys jys    0 Jan 11 14:22 file.txt

4.3 读懂 ls -l 的输出

这一行的信息密度很高,值得逐段拆开。后面讲权限时会一直用到它:

复制代码
-rwxr-xr--  1  owner  group  4096  Sep 16 15:30  index.html
│           │    │      │      │        │             │
│           │    │      │      │        │             └── 文件名
│           │    │      │      │        └── 最后修改时间
│           │    │      │      └── 文件大小(字节)
│           │    │      └── 所属组
│           │    └── 所有者
│           └── 硬链接数
│
└── 文件类型 + 权限
    │
    ├─ -        文件类型:普通文件
    │
    └─ rwxr-xr--
       ├─ rwx    所有者 (owner):读 ✓  写 ✓  执行 ✓
       ├─ r-x    所属组 (group):读 ✓  写 ✗  执行 ✓
       └─ r--    其他人 (others):读 ✓  写 ✗  执行 ✗

从左到右依次是:文件类型与权限位、硬链接数、所有者、所属组、大小(字节)、最后修改时间、文件名。开头那十个字符里,第一个是文件类型,剩下九个分三组,分别是所有者、组、其他人的权限。

4.4 cd:改变工作目录

bash 复制代码
$ cd ..                  # 回到上一级
$ cd /home/jys/test      # 绝对路径
$ cd ../temp/            # 相对路径
$ cd ~                   # 直接回家目录
$ cd -                   # 回到最近一次待过的目录

cd - 用于在两个目录之间来回切换:

bash 复制代码
$ pwd
/home/jys
$ cd /
$ cd -                   # 回到 /home/jys
$ cd -                   # 又回到 /

顺带一个容易忽略的约定:每新建一个用户,系统会在 /home 下创建以用户名命名的工作目录;而 root 用户在安装系统时就已经内置了工作目录 /root

bash 复制代码
$ ls /home
jys  litao  wangwu

5. 创建与删除:touch、mkdir、rmdir、rm

5.1 touch:创建文件,或拨动时间

bash 复制代码
touch newFile.txt        # 文件不存在就创建空文件
touch -a newFile.txt     # 只改访问时间
touch -m newFile.txt     # 只改修改时间

touch 看似简单,背后却是文件系统里一个常被忽略的设计:每个文件有三个时间戳。用 stat 可以看得很清楚:

bash 复制代码
$ stat newFile.txt
  File: 'newFile.txt'
  Size: 0          Blocks: 0      IO Block: 4096  regular empty file
Device: fd01h/64769d  Inode: 1844719   Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1003/     jys)   Gid: ( 1003/     jys)
Access: 2023-01-11 15:06:45.832787572 +0800
Modify: 2023-01-11 15:06:45.832787572 +0800
Change: 2023-01-11 15:06:45.832787572 +0800
 Birth: -
  • Access(atime):最后一次读取文件内容的时间
  • Modify(mtime):最后一次修改文件内容的时间
  • Change (ctime):最后一次修改文件元数据的时间

这里有一个容易被忽略的地方。执行 touch -a 只修改访问时间,但 Change 也会随之改变:

bash 复制代码
$ touch -a newFile.txt
$ stat newFile.txt
Access: 2023-01-11 15:07:00.062846804 +0800    # 变了
Modify: 2023-01-11 15:06:45.832787572 +0800    # 没变
Change: 2023-01-11 15:07:00.062846804 +0800    # 也变了

原因在于 Change 记录的是 inode 本身的变更。你修改了 atime,atime 是存在 inode 里的,所以 inode 被改动了,ctime 自然要更新。mtime 和 ctime 不是一回事:前者是内容,后者是元数据,这个区分在排查问题时经常用得上。

5.2 mkdir:创建目录

bash 复制代码
mkdir mydir                       # 创建单个目录
mkdir -p path1/path2/path3/path4  # 级联创建,父目录不存在会自动建

-p 很实用。没有它,mkdir a/b/ca 不存在时会直接报错;有它,整条路径会被一次性建好。

bash 复制代码
$ mkdir -p path1/path2/path3/path4
$ tree path1
path1
└── path2
    └── path3
        └── path4

tree 不是系统自带的,需要单独装。root 用户直接 yum install -y tree,普通用户加 sudo 前缀。

5.3 rmdir:只能删空目录

bash 复制代码
rmdir mydir                    # 删除空目录
rmdir -p path1/path2/path3/path4   # 连同变空的父目录一起删

它的限制很明确:目录非空时无法删除

bash 复制代码
$ touch path1/myfile.txt
$ rmdir -p path1/path2/path3/path4
rmdir: failed to remove directory 'path1/path2': Directory not empty

5.4 rm:删除工具

bash 复制代码
rm file.txt        # 删除文件
rm -i file.txt     # 删除前询问
rm -r dir          # 递归删除目录及其内容
rm -f file.txt     # 强制删除,不询问
rm -rf path1       # 递归强制删除,几乎不问

对比一下 rmrmdir 的行为差异,能看出一致的设计取向:

bash 复制代码
$ rm dir
rm: cannot remove 'dir': Is a directory      # 不加 -r 删不了目录
$ rm -r dir                                  # 加 -r 才行
$ rm -f path1
rm: cannot remove 'path1': Is a directory    # -f 也不能替代 -r
$ rm -rf path1                               # 两个都需要

-r-f 是两件事:-r 管递归,-f 管是否询问和是否忽略只读属性。它们不能互相替代。

rm -rf 需要单独提醒。它几乎不会给出任何确认机会,而 Linux 下没有回收站,删除后不可恢复。使用之前务必用 pwd 确认位置、用 ls 确认内容,并尽量避免与通配符一起使用。

6. 复制与移动:cp、mv

6.1 cp:复制

bash 复制代码
cp myfile.txt myfile-backup.txt    # 复制并改名
cp *.txt dir/                      # 通配符匹配多个文件,复制到目录
cp -r source_dir target_dir        # 递归复制目录
cp -i a.txt backup/a.txt           # 覆盖前询问
cp -f a.txt backup/a.txt           # 强制覆盖,不询问

* 是通配符,匹配任意长度的字符;? 匹配单个字符。cp a*.txt dir/ 会把所有以 a 开头的 .txt 文件一起复制过去。

默认行为要注意:如果目标文件已存在,cp 会直接覆盖,不问一声

bash 复制代码
$ echo "hello jys" > myfile.txt
$ cp myfile.txt myfile-backup.txt
$ cat myfile-backup.txt
hello jys                       # 原内容被无声覆盖了

要避免这种沉默覆盖,用 -i

bash 复制代码
$ cp -i myfile.txt myfile-backup.txt
cp: overwrite 'myfile-backup.txt'? y

6.2 mv:移动或重命名

mv 是 move 的缩写,但它的行为取决于第二个参数是什么:

  • 第二个参数是文件名:完成重命名,源只能有一个
  • 第二个参数是已存在的目录:把源移动进去,源可以有多个
bash 复制代码
mv myfile.txt yourfile.txt    # 重命名
mv myfile.txt temp/           # 移动到目录
mv temp ../                   # 目录也能移动
mv -i a.txt b.txt             # 目标存在时询问

重命名时同样有个陷阱:如果目标名已存在,改名等于覆盖。

bash 复制代码
$ touch myfile.txt yourfile.txt
$ mv yourfile.txt myfile.txt    # myfile.txt 的内容被 yourfile.txt 顶掉了

在意的话,同样用 -i 让系统先问一句。

7. 读文件:cat、more、less、head、tail

7.1 cat:一次性输出

bash 复制代码
cat temp.txt       # 输出全部内容
cat -b temp.txt    # 非空行编号,空行不编号
cat -n temp.txt    # 所有行编号,空行也编号
cat -s temp.txt    # 多行连续空行压缩成一行

-b-n 的区别只在空行上,但差别很直观。假设文件中间有两行空行:

bash 复制代码
$ cat -b temp.txt
    1  hello jys
    2  hello jys
    3  hello jys
    4  hello jys        # 空行被跳过,不占编号
    5  hello jys

$ cat -n temp.txt
    1  hello jys
    2  hello jys
    3  hello jys
    4                   # 空行照样占一个编号
    5
    6  hello jys
    7  hello jys

-s 则用来收拾混乱的空行:

bash 复制代码
$ cat -s temp.txt      # 连续五个空行会被压成一个

7.2 more 与 less:分页查看

cat 的问题在于把整个文件一次性输出到终端。用 cat 打开几百 MB 的日志,终端会长时间无响应。

more 能分页,但只能向后翻,无法回看less 才是 Linux 中通用的查看工具:

bash 复制代码
less -N app.log        # 显示行号
less -i app.log        # 搜索时忽略大小写

less 的优势在于交互。进入之后:

操作 作用
/字符串 向下搜索
?字符串 向上搜索
n 重复上一次搜索
N 反向重复上一次搜索
PageUp / PageDown 前后翻页
q 退出

还有一个工程上的优势:less 在查看之前不会加载整个文件。打开几十 GB 的日志时,这一点决定了它是否可用。

7.3 head 与 tail:取头取尾

bash 复制代码
head temp.txt          # 默认前 10 行
head -5 temp.txt       # 前 5 行
tail temp.txt          # 默认后 10 行
tail -3 temp.txt       # 后 3 行
tail -f app.log        # 持续跟踪追加内容,Ctrl+C 退出

tail -f 在运维中使用频率极高,它让文件末尾的新内容实时显示在屏幕上,查看日志基本依靠它。

7.4 组合起来解决实际问题

有一个经典问题:如何显示文件的第 180 到 200 行?

单个命令无法完成,但把两个组合起来就可以:

bash 复制代码
$ head -200 temp.txt | tail -20
hello 180
hello 181
...
hello 199

先取前 200 行,再从这 200 行里取最后 20 行,落下来的正好是第 180 到 200 行。这里的 | 是管道,它把左边命令的输出直接交给右边命令当输入。

每个命令只做一件事,剩下的交给组合。这是 UNIX 留下来的思路,后面 greptail -f 用的是同一个方法。

8. 查找与别名

8.1 find:在目录树里找文件

bash 复制代码
find ~ -name test.c              # 在家目录下按名字找
find . -name '*.c'               # 当前目录及子目录下所有 C 文件
find /var/log -type f -mtime -1  # 一天内修改过的普通文件

find 会实际遍历目录树并访问磁盘,在大文件系统上可能很慢。如果需要在几十 GB 的磁盘上运行,通常会把它放到后台执行。

8.2 which 与 whereis:定位程序

两个命令容易混,区别在于找的范围:

bash 复制代码
$ which ls
alias ls='ls --color=auto'
       /usr/bin/ls

$ whereis ls
ls: /usr/bin/ls /usr/share/man/man1/ls.1.gz /usr/share/man/man1p/ls.1p.gz

which 回答「当前 PATH 里会执行哪个程序」,whereis 回答「这个程序相关的文件都散落在哪」------二进制、源码、手册页一起列出来。

8.3 alias:给命令起短名

bash 复制代码
alias hello='ls -a -l -n'    # 定义别名
alias ll='ls -alF'           # 最常用的一个
which hello                  # 可以看到别名确实生效了
unalias hello                # 取消别名

注意 which 的输出会先显示别名定义、再显示真实路径,这说明 Shell 在查找命令时是先看别名、再看 PATH

别名默认只在当前 Shell 会话中有效。需要长期生效时,写进 ~/.bashrc,然后执行 source ~/.bashrc 重新加载。

8.4 grep:在文件里找内容

find 找的是文件,grep 找的是文件里的行:

bash 复制代码
grep "abcd" temp.txt      # 基本查找
grep -i "abcd" temp.txt   # 忽略大小写
grep -n "abcd" temp.txt   # 输出行号
grep -v "abcd" temp.txt   # 反向选择:输出不含该字符串的行

假设文件内容是 abcd / ABCD / hello / jys / 1234 五行,几个选项的效果对比就很清楚了:

bash 复制代码
$ grep -n "abcd" temp.txt
1:abcd

$ grep -ni "abcd" temp.txt
1:abcd
2:ABCD

$ grep -v "abcd" temp.txt
ABCD
hello
jys
1234

$ grep -vni "abcd" temp.txt
3:hello
4:jys
5:1234

选项可以叠加,-vni 就是「反向 + 显示行号 + 忽略大小写」三合一。

9. 时间与系统信息

9.1 date:格式化时间与时间戳

bash 复制代码
date                       # Wed Jan 11 18:22:50 CST 2023
date +%Y/%m/%d             # 2023/01/11
date +%Y/%m/%d-%H:%M:%S    # 2023/01/11-18:23:20

常用的格式标记:

标记 含义
%Y 完整年份(0000...9999)
%m 月份(01...12)
%d 日(01...31)
%H 小时(00...23)
%M 分钟(00...59)
%S 秒(00...61)
%F 等价于 %Y-%m-%d
%X 等价于 %H:%M:%S

时间戳是另一块常用功能:

bash 复制代码
$ date +%s                   # 1673432636
$ date +%Y/%m/%d-%H:%M:%S -d @0
1970/01/01-08:00:00
$ date +%Y/%m/%d-%H:%M:%S -d @1000000000
2001/09/09-09:46:40

Unix 时间戳的定义是:从 1970 年 1 月 1 日 UTC 午夜开始所经过的秒数,不考虑闰秒 。上面 @0 显示成 08:00:00 而不是 00:00:00,是因为系统时区是东八区。

date -s 能设置系统时间,但只有 root 有权限,普通用户只能查看。

9.2 cal:日历

bash 复制代码
cal            # 当前月
cal 1949       # 某一年
cal -3         # 前一个月、当前月、下一个月
cal -y         # 当前整年
cal -j         # 显示一年中的第几天

9.3 系统信息与资源

bash 复制代码
uname -a           # 内核名称、主机名、版本、硬件名、处理器类型等
lsb_release -a     # 发行版信息
top -d 1 -n 5      # 每隔 1 秒刷新,共刷新 5 次

top 默认会一直刷新,按 q 退出。-d 控制刷新间隔,-n 控制刷新次数。加上这两个参数,它就能被脚本调用了。

浮点运算可以交给 bc

bash 复制代码
$ echo 'scale=4; 10 / 3' | bc
3.3333

9.4 关机

bash 复制代码
shutdown -h now        # 停掉服务后立即关机
shutdown -r now        # 重启
shutdown -t 60         # 60 秒后关机

10. 打包与压缩

这两个概念经常混淆,但它们实际上是两步:

  • 打包:把一堆文件合成一个文件,体积基本不变
  • 压缩:用算法把体积变小

tar 首先是打包工具,再通过选项去调用压缩程序。

10.1 tar 的参数速记

bash 复制代码
tar -cvf archive.tar dir/        # 只打包,不压缩
tar -zcvf archive.tar.gz dir/    # 打包并用 gzip 压缩
tar -jcvf archive.tar.bz2 dir/   # 打包并用 bzip2 压缩
tar -ztvf archive.tar.gz         # 不解包,直接看内容
tar -zxvf archive.tar.gz -C /tmp # 解压到指定目录

参数含义:

参数 作用
-c 创建归档(create)
-x 解开归档
-t 查看归档内的文件列表
-z 用 gzip 压缩 / 解压
-j 用 bzip2 压缩 / 解压
-v 显示处理过程
-f 指定归档文件名,后面必须紧跟文件名
-C 解压到指定目录

-f 的位置是新手最容易出错的地方:它后面必须立即接文件名,中间不能再插别的参数。

打包时如果用了绝对路径,tar 会打印一句警告:

复制代码
tar: Removing leading `/' from member names

这是刻意为之:归档里的路径会自动去掉开头的 /,免得解压时覆盖系统原有文件。

10.2 只解压其中一个文件

tar 允许你从归档里挑文件解出来。先用 -t 看清结构:

bash 复制代码
$ tar -ztvf /tmp/etc.tar.gz
...
etc/passwd
...

然后指定路径解压:

bash 复制代码
$ tar -zxvf /tmp/etc.tar.gz etc/passwd

注意路径写的是 etc/passwd,不带开头的 /,原因就是上面那条警告。

10.3 zip 与 unzip

跨平台交换文件时,ZIP 格式兼容性更好:

bash 复制代码
zip -r project.zip project/     # 递归压缩目录
unzip project.zip -d /tmp       # 解压到指定目录

如果需要在 Windows 和 Linux 之间传文件,rzsz 工具能让上传下载变成拖拽操作。

11. 权限:Linux 的安全模型

前面讲了那么多命令,但真正决定「谁能对什么做什么」的,是权限系统。

11.1 两种用户

Linux 下的用户分成两类:

  • 超级用户 root:可以做任何事,不受限制
  • 普通用户:只能做有限的事

提示符直接表明了身份:# 是 root,$ 是普通用户。这个视觉线索很实用,能让你时刻清楚自己当前持有的权限。

bash 复制代码
su                # 切换到 root(省略用户名)
su litao          # 从 root 切回普通用户

11.2 三种人,三种权限

权限是围绕「人」和「事」两个维度组织的。

先说人。访问者分三类:

符号 身份
u User,文件的所有者
g Group,所有者所在的组
o Others,其他用户

再说事。基础权限就三个:

权限 权重 对文件 对目录
r 4 读取文件内容 浏览目录内的信息
w 2 修改文件内容 删除、移动目录内的文件
x 1 执行文件 进入目录(cd)

同样一个 w,落在文件和目录上完全是两件事,最后一列尤其要看清楚。粘滞位就是从这个差别里长出来的。

不具某项权限时,用 - 占位。

11.3 权限的两种写法

九位权限位,每三位一组,每组权重固定是 4、2、1:

rwxr-xr-- 换算成数字就是:u = 4+2+1 = 7,g = 4+1 = 5,o = 4,合起来 754

反过来,看到 644 就知道是 rw-r--r--

11.4 文件类型也在第一位

ls -l 开头那个字符不只表示权限,第一位是文件类型:

字符 类型
- 普通文件
d 目录
l 软链接(类似 Windows 快捷方式)
b 块设备(硬盘、光驱等)
c 字符设备(屏幕等串口设备)
p 管道文件
s 套接口文件

无法确定文件的类型时,可以交给 file 命令,它按内容判断,不依赖扩展名:

bash 复制代码
$ file image.bin

11.5 chmod:改权限

有两种写法,对应两种使用习惯。

数字写法适合一次性设定完整权限:

bash 复制代码
chmod 664 abc.txt     # rw-rw-r--
chmod 640 abc.txt     # rw-r-----
chmod 755 run.sh      # rwxr-xr-x
chmod -R 750 private/ # 递归修改目录及内容

符号写法适合增量修改:

bash 复制代码
chmod u+w abc.txt     # 给所有者加写权限
chmod o-x abc.txt     # 去掉其他人的执行权限
chmod a=x abc.txt     # 所有人的权限都设为仅执行

符号写法的结构是「用户符号 + 操作符 + 权限字符」:

  • 用户符号:u(所有者)、g(同组)、o(其他人)、a(所有人)
  • 操作符:+ 增加、- 取消、= 直接赋值

一个限制要记住:只有文件的拥有者和 root 才能修改文件权限

11.6 chown、chgrp、umask

bash 复制代码
chown user1 f1              # 改所有者
chown -R user1 filegroup1   # 递归改所有者
chgrp users /abc/f2         # 改所属组
chgrp -R users dir/         # 递归改所属组

umask 是最容易被忽略的一个,它决定了新建文件默认拿到什么权限

新建文件的基础权限是 0666,新建目录是 0777。但实际创建出来往往不是这个值,因为还要被 umask 削一刀:

规则是:基础权限 & ~umask (取反再按位与)。umask 里为 1 的位,会把对应权限位清零。

bash 复制代码
umask          # 查看当前掩码
umask 044      # 设置掩码

超级用户默认掩码是 0022,普通用户默认 0002。所以 root 新建的目录通常是 755,文件是 644。

11.7 sudo:按需分配权力

root 权限强大也危险。更好的做法是让普通用户按需借用特定权限,配置文件是 /etc/sudoers

复制代码
# 格式:接受权限的用户  登陆的主机 = (执行命令的用户)  命令

配置好之后:

bash 复制代码
sudo -u root /usr/sbin/useradd u2

这样普通用户就能在授权范围内执行特定命令,而不是拿到完整的 root 权限。

12. 一个反直觉的结论:删除文件取决于目录权限

先看目录的三个权限分别意味着什么:

  • 没有 x:无法 cd 进这个目录
  • 没有 r:无法用 ls 查看目录内容
  • 没有 w:无法在目录里创建文件,也无法删除文件

前三点里,第三点可以推出一个不太合理的结论:只要用户对目录有 w 权限,就能删除目录中的任何文件,无论他对该文件本身是否具有写权限。

自己创建的文件,他人却能删除。这在实践中确实会发生:

bash 复制代码
$ chmod 0777 /home/
$ ls -ld /home/
drwxrwxrwx. 3 root root 4096 9月 19 15:58 /home/

$ touch /home/root.c          # root 建一个文件
$ su - litao                  # 切成普通用户 litao
$ rm /home/root.c             # litao 把 root 的文件删了

为什么会这样?因为删除文件修改的是它所在的目录,而不是文件本身。所谓删除,就是从目录项中移除一个名字。起决定作用的是目录的 w 权限,与文件自身的权限无关。

这个设计在共享目录里就是灾难。而 Linux 的解法是粘滞位:

bash 复制代码
$ chmod +t /home/         # 加上粘滞位
$ ls -ld /home/
drwxrwxrwt. 3 root root 4096 9月 19 16:00 /home/

权限位末位变成 t。再看效果:

bash 复制代码
$ su - litao
$ rm /home/root.c
rm: 无法删除"/home/root.c": 不允许的操作

加上粘滞位之后,目录里的文件只能由三种人删除:

  1. 超级管理员
  2. 该目录的所有者
  3. 该文件的所有者

/tmp 是粘滞位最典型的应用场景:它必须让所有人可写,但又不能让任何人删掉别人的东西。

12.1 权限问题的排查顺序

理解了上面的机制,排查权限问题时就有了固定顺序:

  1. 先看目录,再看文件 。目录没有 x 就进不去,此时文件权限再宽松也没用
  2. 区分 rx 对目录的不同含义 。只有 r 没有 x,你能看到目录里的文件名,但读不了内容、也进不去
  3. 删除操作看父目录,不看文件本身

第 2 条最容易出错:误以为有读权限就能进入目录。实际上能否在目录下操作,门槛是 x 而不是 r

13. 常见误区清单

把前面提到的易错点集中列出:

  1. cat 打开大文件 。几百 MB 的日志会使终端长时间无响应,应改用 less
  2. 混淆 >>>。前者覆盖,后者追加
  3. rm -f 删除目录 。删除目录必须用 -r-f 不能替代
  4. 以为 rm 有回收站 。Linux 下删除后不可恢复,执行 rm -rf 之前先 pwdls
  5. 复制目录时漏掉 -rcp 不加 -r 遇到目录会直接报错
  6. 以为 cpmv 会提示覆盖 。默认不提示,需要提示时加 -i
  7. tar-f 后面插入了其他参数-f 必须紧跟文件名
  8. 混淆 mtime 和 ctime。前者记录内容,后者记录元数据
  9. 以为有目录的 r 权限就能进入 。真正决定能否进入的是 x
  10. 只看文件权限就判断能否删除 。删除由父目录的 w 决定

结语

这批指令不必当作单词表来背。它们串在同一条线索上:目录树给每个文件一个唯一的位置,路径让你能精确定位到它,Shell 把你的意图翻译成内核可以执行的动作,权限决定你能否真正操作。这条线索理清了,命令本身反而容易记,因为每条命令要么在操作这棵树,要么在与这套权限打交道。

相关推荐
hi_ro_a2 小时前
基于正倒排索引的boost搜索引擎
linux·c++·搜索引擎·项目
GG-_-Bond2 小时前
9.1kv存储持久化模拟面试
linux·c语言·数据结构·c++
Dola_Zou2 小时前
Linux工控机核心算法防逆向与自动化加固方案
linux·自动化·软件加密
欧米欧3 小时前
解析Linux从umask位运算到粘滞位
linux
江屿风3 小时前
【Linux系统】【Linux进程终止等待详解与程序替换机制初体验】流食般投喂
linux·运维·笔记·系统架构·centos·unix
螺蛳粉 螺蛳粉3 小时前
CentOS 7.9 自建 Yum 源服务器搭建指南
linux·服务器·centos
吴声子夜歌4 小时前
Shell编程实例——脚本编程的附加特性
linux·运维·shell
yunwei374 小时前
eBPF 教程:BPF 调度器入门
linux·后端·性能优化
byte轻骑兵4 小时前
【BlueZ 】sdp 模块:服务发现协议的用户态实现基础
linux·人工智能·bluez·电脑蓝牙·嵌入式蓝牙