Linux 文件操作
前言
- 这篇是「在命令行里干活」的手册:怎么定位自己、怎么看文件、怎么增删改、怎么管用户和权限、怎么搜东西、怎么理解链接。
- 所有结论都在 Ubuntu 26.04.1 LTS(WSL2) 上实测,日期 2026-11-03。命令输出我尽量原样贴,方便对照。
命令的通用形状
1 | 命令 [选项] [参数] |
方括号表示可选,这是所有 Unix 命令的通用形状。几个约定:
| 写法 | 含义 | 例子 |
|---|---|---|
-a |
短选项,单字母 | ls -a |
-a -l 或 -al |
短选项可以合并 | ls -al 等价 ls -a -l |
--all |
长选项,双横线 | ls --all |
-n 5 或 --lines=5 |
带值的选项 | head -n 5 |
-- |
后面的东西一律当参数,不再当选项 | rm -- -dash |
* ? [0-9] |
通配符(由 shell 展开,不是命令自己的功能) | rm *.py |
两个新手一定会撞的:
- 通配符没匹配到任何文件时,shell 会把
*.pyz原样传给命令,于是报错长得像「文件不存在」:
1 | $ rm *.zzz |
- 以
-开头的文件名会被当成选项,正确写法是加./或用--:
1 | $ touch ./-dash && rm ./-dash && echo 删除成功 |
先搞清自己在哪儿:pwd 与 cd
实测四条:
1 | $ cd /etc && pwd |
| 写法 | 去哪 |
|---|---|
cd (不带参数) |
当前用户家目录,实测 /home/loong |
cd - |
上一个目录(靠 shell 变量 OLDPWD 实现,实测 OLDPWD=/var) |
cd ~ / cd |
同上 |
cd /a/b |
绝对路径:以 / 开头,从根开始数 |
cd ../x |
相对路径:以 . 或 .. 开头,从当前目录开始数 |
cd x |
也是相对路径(相对当前目录),不会去 PATH 里找 |
最后一条最容易出错:可执行文件才走 PATH,目录永远按相对/绝对路径解析。所以执行当前目录里的脚本必须写 ./s.sh,光写 s.sh 会 command not found。
ls 的输出到底怎么读
先造三个东西(普通文件、目录、软链接),看 ls -l 的真实输出:
1 | $ ls -l |
用 awk '{print NF}' 数过,一行是 9 个字段(total 4 那行不算):
| 位置 | 内容 | 上面例子里的值 |
|---|---|---|
| 1 | 类型 + 权限(10 个字符) | -rw-r--r-- |
| 2 | 硬链接数 | 1(目录是 2,因为里面还有 . 和 ..) |
| 3 | 属主(用户) | loong |
| 4 | 属组 | loong |
| 5 | 大小(字节;-h 才变成 K/M/G) |
4 |
| 6-8 | 月、日、时间或年 | Nov 3 18:11 |
| 9 | 名字(软链接后面会跟 -> 目标) |
l1 -> f1.txt |
total 4 是这一目录里文件占用的磁盘块总量(单位 KB),不是文件大小之和,新手常误会。
第 2 个字段用 stat 验证更清楚:
1 | $ stat -c '%n inode=%i 硬链接数=%h 权限=%A(%a) 属主=%U 属组=%G 大小=%s 类型=%F' f1.txt d1 l1 |
注意软链接自己的权限永远是 lrwxrwxrwx(777),真正的权限看它指向的文件。
七种文件类型(第一个字符)
原笔记那张类型表是对的,我把每一种都造出来/找出来了:
| 首字符 | 类型 | 实测样本 |
|---|---|---|
- |
普通文件 | -rw-r--r-- 1 loong loong 2 ... reg |
d |
目录 | drwxr-xr-x 2 loong loong 40 ... dir |
l |
符号(软)链接 | lrwxrwxrwx 1 loong loong 3 ... sym -> reg |
c |
字符设备 | crw-rw-rw- 1 root root 1, 3 ... /dev/null |
b |
块设备(原笔记写成「快设备」,是错字) | brw-rw---- 1 root disk 8, 0 ... /dev/sda |
s |
套接字 | srwx------ 1 root root 0 ... /run/systemd/private |
p |
命名管道(FIFO) | prw-r--r-- 1 loong loong 0 ... pipe1(用 mkfifo pipe1 造的) |
数量级感受一下:本机 /dev 里实测 28 个块设备、140 个字符设备、13 个目录、8 个软链接;/run 里 319 个普通文件、119 个软链接。套接字和管道平时不会自己造,它们是进程间通信用的(/dev/log、systemd 的各种 .socket 就是活例子)。
ls 的三个常用选项与 Ubuntu 自带的别名
| 选项 | 作用 |
|---|---|
-a |
全部,含 . 开头的隐藏文件(连 . 和 .. 一起显示) |
-A |
全部但不显示 . 和 ..,脚本里更常用 |
-l |
长格式(上面那 9 个字段) |
-h |
人类可读的大小(配合 -l 或 -s 才有意义) |
-d |
只看目录本身,不进目录(ls -ld /tmp) |
-t / -S |
按时间 / 按大小排序 |
-R |
递归 |
Ubuntu 的 ~/.bashrc 里默认就配好了别名,实测第 78-93 行:
1 | $ grep -nE "^ *alias" ~/.bashrc |
但别名只在交互式 shell 里生效,脚本里不生效——这条是实测的:在脚本里 alias ll='ls -l' 之后调用 ll,得到的是 ll: command not found。所以写 shell 脚本时永远用完整命令。临时绕过别名用反斜杠:\ls;想知道某个名字到底是什么,用 type:
1 | $ type ls |
同一个 type ls 在脚本(非交互 shell)里跑,结果就变成 ls is /usr/bin/ls——因为 .bashrc 里的别名根本没生效。command -v 的对应行为:command -v cd 输出 cd(内建命令也算存在),查不存在的命令时退出码 1,所以脚本里判断命令存在就写 command -v xxx >/dev/null || { echo 缺这个命令; exit 1; }。
查看文件内容
用一个 11 行的文件实测每个命令(内容形如 L1 one … L11 eleven):
| 命令 | 实测输出/行为 | 什么时候用 |
|---|---|---|
cat 文件 |
从头到尾全部打印 | 短文件 |
cat -n 文件 / nl 文件 |
都带行号,实测 cat -A 看到行号和内容之间是制表符(^I) |
想复制带行号的内容 |
tac 文件 |
倒着打印,第一行是 L11 eleven |
看日志(最新的在前) |
head 文件 |
默认 10 行(实测 head 文件 | wc -l = 10) |
看开头 |
head -3 / head -n 3 / head -c 12 |
3 行 / 3 行 / 前 12 个字节(-3 这种省掉 n 的老写法实测同样有效) |
精确取内容 |
tail 文件 |
默认 10 行 | 看结尾 |
tail -f 文件 |
持续跟踪,实测新追加的一行 appended 立刻显示出来 |
看日志必备 |
wc 文件 |
11 22 91 文件名,依次是行数、单词数、字节数 |
统计 |
wc -l/-w/-c/-m |
11 / 22 / 91 / 91 | -m 才是字符数 |
more、less |
本机都装了(/usr/bin/more、/usr/bin/less) |
分页看 |
wc 的字节数与字符数差别,用中文一测就明白:
1 | $ printf '中文测试\n' > cn.txt |
more / less 的差别(原笔记那几条基本对,补两个实测细节):
- 两者都是「一屏一屏看」,
q退出,回车走一行,空格走一屏。 more看完自动退出;less不会,而且less允许用↑/↓往回翻,more只能往前。less里/关键词搜索、n/N上下跳转、G/g首尾、q退出(man 1 less实测写着SPACE or ^V or f or ^F是向前翻一屏)。- 管道里
more不会分页:非终端环境下它直接把内容全打印出来(实测cat lines.txt | more | head -3直接给出前三行)。所以「ls /etc | more」这种写法只在真终端里有意义。 - 现代发行版里
less常常是默认的PAGER,man、git log用的都是它。
日志场景的三板斧,按顺序记:
1 | $ tail -f /var/log/syslog # 实时看新内容 |
文件与目录的增删改
| 命令 | 干什么 | 实测要点 |
|---|---|---|
touch a b c |
建多个空文件;文件已存在则只更新时间戳 | 实测两次 touch t1,mtime 从 18:11:57.84 变成 18:11:58.95,内容不变 |
mkdir d |
建一层目录 | 重名时报 mkdir: dup: File exists;mkdir -p 遇到已存在的目录不报错(实测退出码 0) |
mkdir -p a/b/c |
连中间层一起建 | 实测得到 deep deep/a deep/a/b deep/a/b/c |
rmdir d |
只能删空目录 | 非空时报 rmdir: failed to remove 'deep_copy': Directory not empty |
rm f |
删文件 | 删目录时报 rm: cannot remove 'dup': Is a directory |
rm -r d |
递归删目录 | 生效 |
rm -rf d |
强制递归删,不提示 | 危险命令,路径写错就是灾难 |
cp a b |
复制文件 | — |
cp -r 目录 新目录 |
递归复制目录 | 实测目录结构完整复制 |
cp -a 源 目标 |
归档模式,保留权限/属主/时间 | 实测目标仍是 -rw-r--r-- loong loong |
cp t1 t2 t3 dir/ |
多个文件拷进目录 | 实测 dir 里出现 t1 t2 t3 |
mv a b |
移动或改名(同一个命令) | 实测 mv copy_of_t1 moved.txt 就是改名 |
mv 文件 目录/ |
移动进目录 | — |
交互与强制:-i、-f、-n
实测把答案用管道喂进去:
1 | $ echo n | rm -i askme # 回答 n -> 文件还在 |
cp -n(不覆盖已存在的文件)在新版 GNU coreutils 里被标记为「行为不保证可移植」,官方建议改用 cp --update=none——这是个典型的「老教程里的写法正在变」的例子。
-f 的语义在 rm 和 cp 上并不完全一样:rm -f 是「不提示、忽略不存在的文件」,cp -f 是「目标存在就先删掉再拷」。别把它们当同一种「强制」。
用户与组:三个账本文件
Linux 的账号信息全在纯文本文件里,看懂这三行就够了(实测拆解 loong 这条):
1 | $ grep '^loong:' /etc/passwd |
| 字段 | 含义 | 实测值 |
|---|---|---|
| 1 | 用户名 | loong |
| 2 | 密码占位符(真正的哈希不在这里) | x |
| 3 | UID(用户唯一 id) | 1000 |
| 4 | GID(主组的 id) | 1000 |
| 5 | 备注/GECOS(全名之类,可为空) | 空 |
| 6 | 家目录 | /home/loong |
| 7 | 登录 shell | /bin/bash |
awk -F: '{print NF}' /etc/passwd | sort -u 实测只输出一个 7,说明每行都是 7 个字段。
组信息在 /etc/group,格式是 组名:占位:GID:成员列表:
1 | $ grep -E "^(root|sudo|shadow|loong):" /etc/group |
sudo<span class="github-emoji" alias="x" style="" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/274c.png?v8">❌</span>27:loong 就是「loong 属于 sudo 组,因此能提权」的物理证据,和 id 的输出一致:
1 | $ id loong |
密码哈希在 /etc/shadow(普通用户读不到,权限 640 属组 shadow,见上一篇):
1 | $ head -2 /etc/shadow |
第 2 列的 * 表示「这个账户没有可用密码、不能密码登录」,! 表示「被锁定」。
UID 的分段(实测本机):0 是 root;1-999 是系统账户(本机 100-999 段有 8 个,如 systemd-network 998、polkitd 987);从 1000 开始才是人(loong = 1000)。另外 /etc/subuid、/etc/subgid 里给每个用户预留了一大段次级 id(实测 loong:100000:65536),容器和无特权用户用,日常不用管。
建用户、删用户:useradd 的真实默认值
原笔记写「useradd:新建用户,-d 指定家目录,-m 不存在,-s 指定 shell」,这句太省了。实测各种写法的结果:
1 | $ useradd probe01; echo $? |
三个和直觉不同的点:
- 不加
-m不建家目录(passwd里照样写着/home/probe01,但目录不存在)。 - 默认 shell 是
/bin/sh,不是/bin/bash。 - 影子条目第 2 列是
!:新账户没有密码,登录不了。
各选项实测:
| 写法 | 实测结果 |
|---|---|
useradd -m probe |
建家目录,并从 /etc/skel 复制 .bash_logout .bashrc .profile,权限 750(drwxr-x---) |
useradd -M probe |
明确不建家目录 |
useradd -d /home/x probe |
指定家目录路径 |
useradd -s /bin/zsh probe |
指定 shell;zsh 没装时提示 useradd: Warning: missing or non-executable shell '/bin/zsh',但照样写进 passwd |
useradd -r probe |
系统账户,实测 UID 999、GID 986 |
useradd -g probe01 -G sudo probe05 |
-g 主组、-G 附加组,实测 id 显示 gid=1001(probe01) groups=1001(probe01),27(sudo) |
删用户:
1 | $ userdel probe04 # 只删账户 |
最后那条报错很常见:该用户还有进程在跑(比如你正 su 在他身份下),必须先把进程结束或加 -f,否则删不掉。
组的操作:
1 | $ groupadd probegrp |
gpasswd -a/-d 和 usermod -aG 都能加组,差别是前者改的是组成员列表,后者改的是用户的附加组集合(效果一样,usermod 记得带 -a,不带 -a 的 usermod -G 会覆盖原有附加组,这是经典事故)。
原笔记说 groups 是「查看指定用户组的信息」——不准确,实测:
1 | $ groups probe01 |
groups 用户 列出的是这个用户属于哪些组;要看某个组本身的信息用 getent group 组名。本机 newgrp、sg、groupmems 都没安装(实测),chage(改密码有效期)在。
切用户与提权:su、sudo、visudo
su 是「切换身份并在那个身份下跑命令」,实测三种用法:
1 | $ su - probe01 -c 'id; echo $HOME; pwd; whoami' |
su -(或 su -l)是登录 shell:会读 /etc/profile 和用户的 ~/.profile,并尝试进入它的家目录(上面那句 warning 就是它进不去 probe01 那个不存在的家目录)。不带 - 时只换身份、基本沿用当前环境。日常习惯:切用户就带 -,否则你继承的还是自己那套 PATH。
被禁用的账户(shell 设成 nologin)登录时得到的是:
1 | $ su - probex |
chsh 改 shell 有个细节,实测:
1 | $ chsh -s /usr/sbin/nologin probe05 |
因为合法 shell 列表在 /etc/shells,本机实测内容是:
1 | $ cat /etc/shells |
nologin 不在表里,所以 chsh 会警告(但仍然写进去了)。想彻底禁止某账户登录,除了 shell 设 nologin,更规范的是 usermod -L 或 passwd -l 锁定。
sudo 是「用别人的身份执行一条命令」(默认 root),实测:
1 | $ sudo -l -U loong |
规则文件 /etc/sudoers 里和本机相关的三行:
1 | $ grep -nE "^(root|%admin|%sudo)" /etc/sudoers |
%sudo 表示 sudo 这个组,所以「把用户加进 sudo 组」=「给他 sudo 权限」,链条是:usermod -aG sudo 用户 → /etc/group 里出现他 → 匹配 %sudo 这行 → 能跑所有命令。
改 sudoers 必须用 visudo,它会先做语法检查再原子写入,直接 vim /etc/sudoers 写错一行,全系统提权就废了。不想打开编辑器也能验证:
1 | $ visudo -c |
visudo 用哪个编辑器打开,由 alternatives 决定,实测本机默认是 nano:
1 | $ readlink -f /usr/bin/editor |
想换成 vim:sudo update-alternatives --config editor 按提示选编号,或者 export VISUAL=vim(原笔记那句「用 update-alternatives --config editor 可以修改系统默认编辑器(nano)」是对的)。
密码:passwd、chpasswd、锁定与状态
| 操作 | 命令 | 实测 |
|---|---|---|
| 改自己的密码 | passwd |
交互输入两次,输入时不回显 |
| 改别人的密码 | sudo passwd 用户名 |
需要 root |
| 脚本里非交互设密码 | echo '用户:密码' | chpasswd |
实测退出码 0 |
| RHEL 的写法 | passwd --stdin 用户 |
Ubuntu 上没有这个选项,别照抄 CentOS 教程 |
| 锁定账户 | passwd -l 用户 |
实测 shadow 第 2 列前面多了 ! |
| 解锁 | passwd -u 用户 |
实测对没设过密码的账户会拒绝:见下 |
| 查状态 | passwd -S 用户 |
实测 probey L ...(L=锁定)、probe01 P ...(P=有可用密码) |
passwd -u 那条拒绝信息很有教育意义:
1 | $ passwd -u probe05 |
意思是:这个账户从来没设过密码,只是被 -l 加了个 ! 前缀;直接解锁会变成空密码账户,所以它拒绝,让你先设密码。
/etc/shadow 一行有 9 列,前 3 列日常够用:用户名:哈希:最后修改日期(天):最小间隔:最大有效期:警告天数:失效宽限::(实测 root:*:20692:0:99999:7:::,20692 是从 1970-01-01 起算的天数)。改有效期用 chage,比手改文件安全。
权限:r、w、x 对文件和目录不是一回事
三组权限对应三种身份,这个大家都背得熟:
| 身份 | 字母 | 含义 |
|---|---|---|
| 属主 | u(user) |
文件属于谁 |
| 属组 | g(group) |
文件属于哪个组 |
| 其他人 | o(other) |
剩下的所有人 |
| 全部 | a(all) |
u+g+o |
但同一个字母对文件和目录的意义不同,这一点原笔记没写,而它恰恰是最容易看错的地方。实测:
1 | $ mkdir secdir && printf 'inside\n' > secdir/inner.txt |
结论:
- 对文件:
r能读内容,w能改内容,x能当成程序执行。 - 对目录:
r只允许列出里面的名字,x才允许进入并按名字访问(cd、cat dir/文件)。所以「有 r 没 x」的目录你能看到文件名但打不开任何东西——这是排查「明明 ls 得到文件了为什么 cat 说 Permission denied」的关键。 - 对目录的
w:能不能在里面创建/删除文件(删除时看的是父目录的写权限,不是文件自己的)。
脚本能不能跑,实测:
1 | $ printf '#!/bin/bash\necho ran\n' > s.sh |
所以「bash 脚本.sh」永远能跑,而「./脚本.sh」必须有 x。
chmod:符号写法和八进制写法
符号写法 chmod [身份][操作][权限] 文件:
| 操作符 | 意思 |
|---|---|
+ |
加上这个权限 |
- |
去掉 |
= |
设成(其他未写的权限会被清掉) |
实测一串(起始权限 754):
1 | $ chmod 754 p1 && stat -c '%a %A' p1 |
八进制的原理(原笔记这段是对的,补一张速查):r=4、w=2、x=1,三位二进制合成一个八进制数字。
| 八进制 | 二进制 | 符号 |
|---|---|---|
| 0 | 000 | --- |
| 1 | 001 | --x |
| 2 | 010 | -w- |
| 3 | 011 | -wx |
| 4 | 100 | r-- |
| 5 | 101 | r-x |
| 6 | 110 | rw- |
| 7 | 111 | rwx |
所以 755 = 属主 rwx、属组 r-x、其他人 r-x。递归改目录用 -R:sudo chmod -R 755 目录。
改属主/属组是另外两个命令:
1 | $ sudo chown probe01 file # 只改属主 |
umask:新建文件的默认权限
umask 是「从默认全权限里扣掉的位」。默认值:目录按 777 起算、文件按 666 起算(文件永远不默认给执行位)。实测三组:
1 | $ umask |
算法就是「默认值按位与上 umask 取反」,日常口算用「7 减去每一位」即可:umask 022 → 目录 755、文件 644;umask 077 → 目录 700、文件 600。
原笔记用 0002 举例(目录 775、文件 664)算得没错,只是本机 Ubuntu 的默认 umask 是 0022,不是 0002(RHEL 的「用户私有组」设置才会给到 0002)。
特殊权限位:/tmp 末尾那个 t
1 | $ ls -ld /tmp |
第 10 个字符是 t(大写 T 表示没有其他人执行位时的 sticky),八进制第 1 位是 1。含义是:谁都能在里面建文件,但只有属主能删自己的。共享目录就靠它。另外两个特殊位是 s(setuid/setgid,八进制 4/2)和 t 之外的组合,日常见到 rws 别慌,那是 setuid。
找文件:find
格式是 find [起始目录] [条件] [动作],目录不写就默认从当前目录开始——这条实测很重要:
1 | $ find -maxdepth 2 -name passwd # 原笔记的写法,少写了路径 |
所以「sudo find -maxdepth 2 -name passwd」并不是「全盘搜索」,它搜的是你当前所在目录。要全盘搜必须写 /。
常用条件,全部实测过:
| 条件 | 作用 | 实测例子 |
|---|---|---|
-name / -iname |
按名字(-iname 忽略大小写) |
find /etc -maxdepth 2 -iname "*passwd*" 找到 /etc/passwd、/etc/passwd-、/etc/security/opasswd |
-maxdepth N |
只往下找 N 层 | -maxdepth 1 只看当前层 |
-type |
按类型(f 普通、d 目录、l 链接、s 套接字、p 管道、c/b 设备) | 当前目录实测:f 17 个、d 12 个、l 2 个、p 1 个 |
-size |
按大小,+/- 表示大于/小于,单位 c 字节、k、M、G |
造了个 9 KB 的 big.bin,-size +5k 命中它 |
-mmin / -mtime |
最近 N 分钟/天修改过 | -mmin -5 列出了刚建的文件 |
-amin / -atime |
访问过 | 注意很多文件系统挂了 relatime,访问时间不会每次都更新 |
-cmin / -ctime |
状态改变(改权限、改属主)过 | 用 chmod 之后再查就能命中 |
-user / -group |
按属主/属组 | find /etc -maxdepth 1 -user root |
-exec 命令 {} \; |
对每个结果执行一条命令 | find . -name "*.txt" -exec basename {} \; 输出 two.txt、one.txt |
-exec 命令 {} + |
同上但一次传多个参数(快得多) | find . -name "*.txt" -exec wc -l {} + 输出带 total |
-delete |
直接删掉找到的 | 实测执行后目录空了 |
三个提醒:
-mtime +7是「7 天之前修改的」,-mtime -7才是「最近 7 天内」,+/-的方向和直觉相反,务必记牢。-name要加引号(-name "*.txt"),否则会被 shell 先展开,结果完全不同。find / -name xxx会刷一堆Permission denied,习惯写法是加2>/dev/null。
找命令与找内容:whereis、which、grep
找「命令在哪、文档在哪」,四个工具各有分工,实测:
1 | $ whereis ls |
原笔记里 whereis ls 的结果写的是 /bin/ls,现在实测是 /usr/bin/ls(/bin 只是软链接)。选哪个:
- 脚本里判断命令存在与否 →
command -v xxx(POSIX 标准,最可靠)。 - 想知道它是别名/内建/文件 →
type xxx。 - 想连文档、配置一起看 →
whereis xxx。
grep 是「在文件里按模式找行」,实测:
1 | $ grep -n "/bin/bash" /etc/passwd |
退出码这一条在脚本里特别有用:if grep -q "关键词" 文件; then ...; fi。
常用选项:-i 忽略大小写、-n 显示行号、-c 只计数、-v 反选、-r 递归、-w 整词匹配、-A3/-B3 显示匹配行的后/前 3 行(查日志极有用)、--color=auto(Ubuntu 已在 ~/.bashrc 里默认开了别名)。
和 find 的分工:按「名字/属性」找文件用 find,按「内容」找用 grep(内容搜索想跨目录快,可以用 grep -rn,或者装 ripgrep 的 rg,本机未装)。
链接文件:硬链接与软链接
ln 的两种形态,一次全测出来:
1 | $ ln orig.txt hard.txt # 硬链接(不加 -s) |
看懂这张表就看懂了两者的区别:
- 硬链接和原文件是同一个 inode(63),只是多了一个名字,链接数从 1 变 2。改任一个,另一个立刻变(实测
printf 'changed\n' > orig.txt之后hard.txt也是changed)。 - 软链接是独立文件(inode 64),里面存的是目标路径字符串(
readlink soft.txt实测返回orig.txt,是相对路径)。
行为差异,全部实测:
| 场景 | 硬链接 | 软链接 |
|---|---|---|
| 删掉原文件 | 内容还在,cat keep_hard.txt 正常输出(数据要等所有链接都删掉才释放) |
变成悬空链接:cat dangling.txt → No such file or directory |
| 指向目录 | 拒绝:ln: adir: hard link not allowed for directory |
可以:ln -s adir dirsoft 正常 |
| 跨文件系统 | 拒绝:ln: failed to create hard link ...: Invalid cross-device link(实测从 ext4 往 /mnt/e 建失败) |
可以(存的是路径,不关心目标在哪) |
| 权限显示 | 和原文件一样 | 永远是 lrwxrwxrwx(777),真实权限看目标 |
| 常见用途 | 几乎不用(cp -l 做快照省空间时会用) |
大量使用:/bin→/usr/bin、update-alternatives、/dev/log |
系统里软链接有多普遍,实测 /usr/bin 下 257 个是软链接,例如:
1 | $ find /usr/bin -maxdepth 1 -type l | head -5 |
(vimdiff、gprof 这类都是 alternatives 机制的软链接,指向真正的实现。)
查看链接指向的三个命令:
1 | $ readlink soft.txt # 只打印链接里存的路径 |
原笔记那句「软连接」是错字,应为软链接(soft link / symbolic link),硬链接是 hard link。
常见坑速查
ls -l第二列当成「用户组」:那是硬链接数,属主和属组在第 3、4 列。wc的第三个数当字符数:是字节数,中文一测就露馅(-m才是字符数)。find -maxdepth 2 -name xxx忘了写路径:它从当前目录开始找,不是全盘。-mtime +7理解成「最近 7 天」:它是「7 天以前」。-name *.txt不加引号:会被 shell 先展开,结果不可控。- 照抄
passwd --stdin:Ubuntu 没有,用chpasswd。 useradd之后以为有 bash 和家目录:实测默认 shell 是/bin/sh、不建家目录,要-m -s /bin/bash。usermod -G忘了-a:会覆盖掉该用户原有的附加组。- 直接
vim /etc/sudoers:写错一行就没人能 sudo 了,用visudo(或visudo -c检查)。 rm -rf手滑:路径带变量时先echo一遍再执行;生产机上考虑alias rm='rm -i'。- 以为目录有
r就能进:cd和访问里面的文件需要x(实测 chmod 600 的目录ls能列名但cd报 Permission denied)。 ./脚本.sh报 Permission denied:缺执行位,chmod +x或者直接用bash 脚本.sh。- 删不掉用户报「currently used by process」:他还有进程在跑,先退出那个会话或结束进程。
- 软链接「坏了」找不到原因:它存的是路径字符串,目标被移动/删除就悬空,用
ls -l、readlink -f、file三个命令查。
练习
- 造一个 11 行的文件,用
wc的四种统计各跑一次,解释为什么-c和-m在纯英文文件上数值相同、在中文文件上不同。 - 用
mkdir -p建a/b/c/d,再想办法只用一条rmdir把它们从最深一层开始全部删掉(提示:rmdir支持多个参数,或者用find -depth -type d -delete)。 - 建一个目录并
chmod 600,验证「能ls但不能cd」;再chmod 700验证反过来需要x。把两次报错原文记下来。 - 用
useradd -m -s /bin/bash建一个练习账户,检查它的家目录权限和从/etc/skel复制来的文件;然后gpasswd -a 用户 sudo给它 sudo 权限,用sudo -l -U 用户验证;最后userdel -r清理干净。 - 分别用
find、whereis、which、command -v、type查ls,把五个输出并排贴出来并说明差别。 - 在
/etc下用find找出所有软链接(-type l),数一下有多少个,再挑 3 个用readlink -f看它们最终指向谁。 - 建硬链接和软链接各一个,然后删掉原文件,分别
cat两个链接,解释结果为什么不同;再用stat -c '%i %h'验证 inode 与链接数。 - 写一条命令:找出
/var/log下最近 1 天内修改过、且大于 10 KB 的普通文件,并对每个执行ls -lh(用-exec ... \;和-exec ... +各写一遍,比较执行次数)。
参考
- GNU findutils 手册(find、xargs 的权威说明)
- GNU coreutils 手册(ls、cp、mv、chmod、wc 等)
- Debian 手册页:
useradd(8)、usermod(8)、userdel(8) - shadow-utils 手册页:
passwd(1)、chpasswd(8)、shadow(5) - sudo 官方手册:
sudoers(5)与visudo - Filesystem Hierarchy Standard 3.0
- Red Hat:修改文件系统访问、权限与所有权(含 umask、特殊权限位)
- Linux 手册页:
inode(7)、symlink(7)、chmod(2) - Ubuntu 文档:Users, Groups 与权限管理