文件系统

Mr.Hope ... 2020-6-3 大约 12 分钟

# 文件系统

# pwd

pwd 命令显示列出当前所在的目录。

pwd
1

# cd

cd 命令用来改变用户所在的目录。

# 进入用户的主目录
$ cd

# 进入前一个工作目录
$ cd -

# 进入指定用户的主目录
$ cd ~user_name
1
2
3
4
5
6
7
8

# ls

ls 目录可以显示指定目录的内容。不加参数时,显示当前目录的内容。

ls
1

上面命令显示当前目录的内容。

ls 命令也可以显示指定文件是否存在。

$ ls foo.txt
foo.txt
1
2

-l 参数可以显示文件的详细信息。

$ ls -l foo.txt
-rw-rw-r-- 1 me   me   0 2016-03-06 14:52 foo.txt
1
2

上面命令输出结果的第一栏,是文件的类型和权限。

文件类型分为以下几种。

  • - 普通文件
  • d 目录
  • l 符号链接。注意,对于符号链接文件,剩余的文件属性总是"rwxrwxrwx"。
  • c 字符设备文件,指按照字节流处理数据的设备,比如调制解调器。
  • b 块设备文件,指按照数据块处理数据的设备,比如硬盘。

其他参数的用法。

# 显示多个目录的内容
$ ls ~ /usr

# -a --all 显示隐藏文件
$ ls -a

# -A 与-a类似,但是不显示当前目录和上一级目录两个点文件
$ ls -A

# -l 显示详细信息
$ ls -l

# -1 单列显示,每行只显示一个文件
$ ls -1

# -d 显示当前目录本身,而不是它的内容
# 通常与-l配合使用,列出一个目录本身的详细信息
$ ls -dl

# -F 目录名之后添加斜杠,可执行文件后面添加星号
$ ls -F

# -h 与-l配合使用,将文件大小显示为人类可读的格式

# -t 按文件修改时间排序,修改晚的排在前面
$ ls -t

# -s 按文件大小排序,

# --reverse 显示结果倒序排列
$ ls -lt --reverse
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

如果只显示一个目录里面的子目录,不显示文件,可以使用下面这些命令。

# 只显示常规目录
$ ls -d */
$ ls -F | grep /
$ ls -l | grep ^d
$ tree -dL 1

# 只显示隐藏目录
$ ls -d .*/

# 隐藏目录和非隐藏目录都显示
$ find -maxdepth 1 -type d
1
2
3
4
5
6
7
8
9
10
11

另一个简便方法是利用自动补全功能,先键入 cd 命令,然后连按两下 tab 键。

# stat

stat 命令是加强版的 ls 命令,可以显示一个文件的详细信息。

$ stat timestamp
File: 'timestamp'
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: 803h/2051d Inode: 14265061 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1001/ me) Gid: ( 1001/ me)
Access: 2008-10-08 15:15:39.000000000 -0400
Modify: 2008-10-08 15:15:39.000000000 -0400
Change: 2008-10-08 15:15:39.000000000 -0400
1
2
3
4
5
6
7
8

# touch

touch 用来设置或更新文件的访问,更改,和修改时间。然而,如果一个文件名参数是一个 不存在的文件,则会创建一个空文件。

touch timestamp
1

上面命令创建了一个名为 timestamp 空文件。如果该文件已经存在,就会把它的修改时间设置为当前时间。

mkdir -p playground/dir-{00{1..9},0{10..99},100}
touch playground/dir-{00{1..9},0{10..99},100}/file-{A..Z}
1
2

上面的命令创建了一个包含一百个子目录,每个子目录中包含了 26 个空文件。

# file

file 命令显示指定文件的类型。

$ file picture.jpg
picture.jpg: JPEG image data, JFIF standard 1.01
1
2

# chmod

chmod 命令用于更改文件的权限,是“change mode”的缩写。

chmod 600 foo.txt
1

上面命令将 foo.txt 的权限改成了 600。

chmod 还可以接受四个缩写,为不同的对象单独设置权限。

  • u 所有者“user”的简写
  • g 用户组“group”的缩写
  • o 其他所有人“others”的简写
  • a 所有人“all”的简写
# 为所有者添加可执行权限
$ chmod u+x foo.txt

# 删除所有者的可执行权限
$ chmod u-x foo.txt

# 为所有人添加可执行权限,等价于 a+x
$ chmod +x foo.txt

# 删除其他人的读权限和写权限。
$ chmod o-rw foo.txt

# 设定用户组和其他人的权限是读权限和写权限
$ chmod go=rw foo.txt

# 为所有者添加执行权限,设定用户组和其他人为读权限和写权限,多种设定用逗号分隔
$ chmod u+x,go=rw foo.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

添加权限。

  • +x 添加执行权限
  • +r 设置读权限
  • +w 设置写权限
  • +rwx 设置所有读、写和执行权限。

删除权限只需将 + 更改为 -,就可以删除任何已设置的指定权限。可以使用 -R(或 --recursive)选项来递归地操作目录和文件。

设置精确权限,可以使用=代替 +- 来实现此操作。如果想为用户、组或其他用户设置不同的权限,可以使用逗号将不同表达式分开(例如 ug=rwx,o=rx)。

由于一共有 3 种可能的权限。也可以使用八进制数代替符号来设置权限。通过这种方式设置的权限最多使用 3 个八进制数。第 1 个数定义用户权限,第 2 个数定义组权限,第 3 个数定义其他权限。这 3 个数中的每一个都通过添加想要的权限设置来构造: 读 (4)、写 (2) 和执行 (1)。

  • rwx 7
  • rw- 6
  • r-x 5
  • r-- 4
  • -wx 3
  • -w- 2
  • --x 1
  • --- 0

# umask

umask 用来查看和设置权限掩码。

$ umask
0022
1
2

上面命令显示当前系统之中,默认的文件掩码是 0022,转为二进制就是 000 000 010 010

可以看到,这个掩码是一个 12 位的二进制数,后面的 9 位分别代表文件三种使用对象的三类权限。只要对应位置上是 1,就表示关闭该项权限,所以 010 就表示关闭读权限。

新建文件时,通常不会带有执行权限,也就是说,新建文件的默认权限是 rw-rw-rw-。如果文件掩码是 0022,那么用户组和其他人的写权限也会被拿掉。

$ touch new.txt
$ ls -l new.txt
-rw-r--r-- 1 me   me   0 2016-03-06 14:52 new.txt
1
2
3

上面代码中,new.txt 的用户组和其他人的写权限就没了。

umask 后面跟着参数,就表示设置权限掩码。

umask 0000
1

上面命令将权限掩码设为 0000,实际上就是关闭了权限掩码。

umask 命令设置的掩码值只能在当前 Shell 会话中生效,若当前 Shell 会话结束后,则必须重新设置。

# du

du 命令用于查看指定目录的大小。

du -hs /path/to/directory
1

显示第一层子目录的大小。

du -h --max-depth=1 /path/to/folder
1

参数的含义。

  • -h 表示人类可读的格式
  • -s 表示总结信息,否则会显示该目录内所有文件和子目录的信息。

tree 命令也可以显示子目录大小。

tree --du -h /path/to/directory
1

# md5sum

md5sum 命令用来显示一个文件的 md5 校验码。

$ md5sum image.iso
34e354760f9bb7fbf85c96f6a3f94ece    image.iso
1
2

# locate

locate 程序快速搜索本机的路径名数据库,并且输出每个与给定字符串相匹配的文件名。

$ locate bin/zip
/usr/bin/zip
/usr/bin/zipcloak
/usr/bin/zipgrep
/usr/bin/zipinfo
/usr/bin/zipnote
/usr/bin/zipsplit
1
2
3
4
5
6
7

locate 数据库由另一个叫做 updatedb 的程序创建。大多数装有 locate 的系统会每隔一天运行一回 updatedb 程序。因为数据库不能被持续地更新,所以当使用 locate 时,您会发现 目前最新的文件不会出现。为了克服这个问题,可以手动运行 updatedb 程序, 更改为超级用户身份,在提示符下运行 updatedb 命令。

locate 支持正则查找。--regexp 参数支持基本的正则表达式,--regex 参数支持扩展的正则表达式。

locate --regex 'bin/(bz|gz|zip)'
1

# find

locate 程序只能依据文件名来查找文件,而 find 程序能基于各种各样的属性,搜索一个给定目录(以及它的子目录),来查找文件。

# 输出当前目录的所有子目录和文件(含子目录)
$ find
$ find .

# 显示当前目录的文件总数
$ find . | wc -l

# 当前目录的子目录总数
$ find . -type d | wc -l

# 当前目录的文件总数(不含子目录)
$ find . -type f | wc -l

# 当前目录的文件名匹配“*.JPG”且大于1M的文件总数
$ find . -type f -name "\*.JPG" -size +1M | wc -l
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

-type 参数支持的文件类型。

  • b 块设备文件
  • c 字符设备文件
  • d 目录
  • f 普通文件
  • l 符号链接

-size 参数支持的文件大小类型。

  • b 512 个字节块。如果没有指定单位,则这是默认值。
  • c 字节
  • w 两个字节的字
  • k 千字节
  • M 兆字节
  • G 千兆字节

find 程序支持的查询参数。

  • -cmin n 匹配的文件和目录的内容或属性最后修改时间正好在 n 分钟之前。指定少于 n 分钟之前,使用 -n,指定多于 n 分钟之前,使用 +n。
  • -cnewer file 匹配的文件和目录的内容或属性最后修改时间早于那些文件。
  • -ctime n 匹配的文件和目录的内容和属性最后修改时间在 n*24 小时之前。
  • -empty 匹配空文件和目录。
  • -group name 匹配的文件和目录属于一个组。组可以用组名或组 ID 来表示。
  • -iname pattern 就像-name 测试条件,但是不区分大小写。
  • -inum n 匹配的文件的 inode 号是 n。这对于找到某个特殊 inode 的所有硬链接很有帮助。
  • -mmin n 匹配的文件或目录的内容被修改于 n 分钟之前。
  • -mtime n 匹配的文件或目录的内容被修改于 n*24 小时之前。
  • -name pattern 用指定的通配符模式匹配的文件和目录。
  • -newer file 匹配的文件和目录的内容早于指定的文件。当编写 shell 脚本,做文件备份时,非常有帮助。每次您制作一个备份,更新文件(比如说日志),然后使用 find 命令来决定自从上次更新,哪一个文件已经更改了。
  • -nouser 匹配的文件和目录不属于一个有效用户。这可以用来查找 属于删除帐户的文件或监测攻击行为。
  • -nogroup 匹配的文件和目录不属于一个有效的组。
  • -perm mode 匹配的文件和目录的权限已经设置为指定的 mode。mode 可以用 八进制或符号表示法。
  • -samefile name 相似于-inum 测试条件。匹配和文件 name 享有同样 inode 号的文件。
  • -size n 匹配的文件大小为 n。
  • -type c 匹配的文件类型是 c。
  • -user name 匹配的文件或目录属于某个用户。这个用户可以通过用户名或用户 ID 来表示。
  • -depth 指导 find 程序先处理目录中的文件,再处理目录自身。当指定-delete 行为时,会自动 应用这个选项。
  • -maxdepth levels 当执行测试条件和行为的时候,设置 find 程序陷入目录树的最大级别数
  • -mindepth levels 在应用测试条件和行为之前,设置 find 程序陷入目录数的最小级别数。
  • -mount 指导 find 程序不要搜索挂载到其它文件系统上的目录。
  • -regex 指定正则表达式
# 找出包括空格或其它不规范字符的文件名或路径名
$ find . -regex '.*[^-\_./0-9a-zA-Z].*'
1
2

find 程序还支持逻辑操作符。

  • -and 如果操作符两边的测试条件都是真,则匹配。可以简写为 -a。注意若没有使用操作符,则默认使用 -and。
  • -or 若操作符两边的任一个测试条件为真,则匹配。可以简写为 -o。
  • -not 若操作符后面的测试条件是真,则匹配。可以简写为一个感叹号(!)。
  • () 把测试条件和操作符组合起来形成更大的表达式。这用来控制逻辑计算的优先级。注意 因为圆括号字符对于 shell 来说有特殊含义,所以在命令行中使用它们的时候,它们必须 用引号引起来,才能作为实参传递给 find 命令。通常反斜杠字符被用来转义圆括号字符。
# 或关系
( expression 1 ) -or ( expression 2 )

# 找出不是600权限的文件,或者不是700权限的目录
$ find ~ \( -type f -not -perm 0600 \) -or \( -type d -not -perm 0700 \)
1
2
3
4
5

find 程序的逻辑表达式,具有“短路运算”的特点,即对于 expr1 -operator expr2 这个表达式,expr2 不一定执行。这是为了提高运行速度。

  • expr1 为真,且操作符为 -and,expr2 总是执行
  • expr1 为假,且操作符为 -and,expr2 从不执行
  • expr1 为真,且操作符为 -or,expr2 从不执行
  • expr1 为假,且操作符为 -or,expr2 总是执行

为了方便执行一些常见操作,find 程序定义了一些预定义操作。

  • -delete 删除当前匹配的文件。
  • -ls 对匹配的文件执行等同的 ls -dils 命令。并将结果发送到标准输出。
  • -print 把匹配文件的全路径名输送到标准输出。如果没有指定其它操作,这是 默认操作。
  • -quit 一旦找到一个匹配,退出。
# 找到匹配的文件,并显示在标准输出
# -print 是默认操作,可以省略
$ find . -print

# 删除后缀名为BAK的文件
# 执行 delete 操作前,最好先执行 print 操作,确认要删除哪些文件
$ find . -type f -name '*.BAK' -delete
1
2
3
4
5
6
7

预定义操作可以与逻辑表达式,结合使用。

find ~ -type f -and -name '*.BAK' -and -print
1

除了预定义操作以外,用户还可以使用 -exec 参数自定义操作。

-exec command {} ;
1

上面的命令中,command 是一个命令行命令,{} 用来指代当前路径,分号表示命令结束。

# 预定义的 -delete 操作,等同于下面的操作
-exec rm '{}' ';'
1
2

-exec 使用时,每次找到一个匹配的文件,会启动一个新的指定命令的实例。

find ~ -type f -name 'foo*' -exec ls -l '{}' ';'
1

执行上面的命令,ls 程序可能会被调用多次。

ls -l file1
ls -l file2
1
2

如果想改成 ls 程序只调用一次,要把 find 命令里面的分号,改成加号。

$ ls -l file1 file2
# 相当于
$ find ~ -type f -name 'foo*' -exec ls -l '{}' +
1
2
3

# xargs

xargs 命令从标准输入接受输入,并把输入转换为一个特定命令的参数列表。

find ~ -type f -name 'foo\*' -print | xargs ls -l
1