Linux随笔2 - Linux的目录配置、文件元数据信息、符号链接与硬链接和常用文件和目录管理命令示例

1. Linux的目录配置与FHS(Filesystem Hierarchy Standard)

由于Linux发行版众多,如果各个发行版都按照各自开发人员的习惯随意命名目录以及分配目录的作用,那么势必造成学习、交流和使用上的不便。为此,就需要一个统一的目录配置标准来约束Linux的目录配置中的目录命名以及角色划分,而这个统一的目录配置标准就是大名鼎鼎的FHS(Filesystem Hierarchy Standard)了,即文件系统分级标准。

FHS标准指出,其主要目的是希望用户可以了解到已经安装的软件通常位于哪个目录,即该标准的重点是规范每个特定目录下应该存放什么数据,即该目录在系统中的角色是什么。FHS依据目录使用的频繁与否以及是否允许用户随意修改,将目录划分为4种形态:

是否可变\是否可共享可共享(shareable)不可共享(unshareable)
不可变(static)/usr(用于存放软件等)/etc(用于存放配置文件等)
/opt(第三方软件安装路径)/boot(存放系统启动引导与内核文件)
可变的(variable)/var/mail(邮箱)/var/run(程序运行相关资料)
/var/spool/news(新闻组,可以没有)/var/lock(程序运行的进程锁)

关于上述四种形态,解释如下:

  • 可共享:可以通过NFS等网络存储方式给其他主机挂载使用的目录
  • 不可共享:不可以通过NFS等网络存储方式给其他主机挂载使用的目录
  • 不可变:有些数据不会经常变动,而是随着发行版而变动,比如库函数、主机服务配置文件等
  • 可变的:随着使用过程而经常被修改的文件,比如日志文件等

实际上,FHS针对目录树架构只是给出了3层目录种应该存放的内容,如下所示:

  1. / (root,根目录):与系统启动有关,也是文件系统挂载的最顶层目录
  2. /usr (Unix Software Resource):与软件安装与执行有关
  3. /var (variable):与系统运行过程有关

而Linux目录配置将会基于上述三方面展开。

1.1 根目录(/)的意义与内容

顾名思义,根目录衍生出了所有其他目录,而且与系统的启动、还原和修复等操作相关。由于系统启动时需要特定的启动软件、内核文件、启动所需要的程序和库函数等文件,如果系统出现问题需要修复,那么根目录种需要包含能够修复文件系统的程序才行。所以FHS标准要求根目录不要放在非常大的分区内,单独分区,确保其中不要存储太多的用户数据;而且应用程序安装的软件最好不要与根目录放在同一个分区中,保持根目录越小越好;另外,在分区根目录的时候,尽量将根目录分为LVM,这样方便以后对根目录进行必要的扩展。

FHS对于根目录中应该包含那些目录,做了不同的要求,有些目录是必须要存在的,即便没有物理目录存在,也需要构建出链接目录;而对于另外一些目录,则是建议存在的目录,不做强制要求。

1.1.1 FHS要求根目录中必须存在的目录

这部分目录的名字以及其角色作用,如下表所示:

目录应该存放的文件
/bin系统有很多存放执行文件的目录,但是/bin比较特殊,其中包含了单用户模式下话能够被使用的命令。在/bin目录下的命令可以被root用户以及一般用户使用,主要命令:cat, chmod, chown, date, mv, mkdir, cp, bash
/boot放置启动过程需要使用的文件,包括Linux内核文件以及启动选项与启动所需要的配置文件等。Linux内核常用的文件名为:vmlinuz,如果使用的是grub2这个引导程序,那么在/boot目录下还会存在一个/boot/grub2/这个目录
/devLinux系统上的设备和接口设备都是以文件的形式存在于这个目录下的,通过读写这个目录下的某个文件,就相当于读写某个设备。其中比较重要的文件有==/dev/null, /dev/zero, /dev/tty,/dev/loop*, /dev/sd*==等
/etc系统主要的配置文件几乎都存在这个目录中,比如记录用户名和密码等信息的/etc/passwd文件和各种服务的配置文件等等。一般来说,这个目录的内容是可以被一般用户查看的,但是只有root用户有权限进行修改。不要将可执行的二进制程序放置在这个目录中。FHS标准还建议下面几个目录最好存在这个目录中:
- /etc/opt/(必要):这个目录用于存放第三方软件的相关配置文件
- /etc/X11/(建议):与X Window有关的各种配置文件基本都存放在这个目录中,尤其xorg.conf这个X Server的配置文件
- /etc/sgml/(建议):与SGML格式有关的各项配置文件
- /etc/xml/(建议):与XML格式有关的各项配置文件
/lib用于存放系统库函数,另外FHS要求该目录下面必须存在:
- /lib/modules/:这个目录主要放置模块化的内核相关模块(驱动程序等)
/media用于挂载光盘、DVD等设备,常见的命名方式为/media/cdrom
/mnt用于暂时挂载额外的存储设备,比如移动硬盘等等,用途与media目录相同
/opt第三方软件的安装目录,不过通常的习惯是将软件安装在/usr/local目录下
/run早期的FHS标准建议系统启动运行所产生的各项信息应该存放在/var/run这个目录中;新版的FHS标准则建议用/run目录替代/var/run这个目录。由于/run目录可以使用内存模拟,所以性能会好很多
/sbin该目录下存放了启动过程所需要的命令,其中包含启动、修复以及还原系统所需要的命令。本机自行安装的软件所产生的系统执行文件,一般放置在/usr/local/sbin这个目录中。常见的命令包括fdisk, fsck, ifconfig, mkfs
/srv可以视为service的缩写,一些网络服务启动之后,其所需要的数据一般存放在这个目录中,比如web服务、ftp服务等。比如web服务所需要的网页数据可以存放在/srv/www这个目录中,但是建议还是最好存放在/var/lib这个目录中
/tmp一般用户或者程序暂时存放文件的地方,这个目录是任何人都能够读写的,所以需要定期清理一下。不要将重要数据存放在这个目录中,因为FHS建议启动时应该将该目录下的内容删除
/usr1.2部分介绍
/var1.3部分介绍

上面是FHS要求存在的目录,此外还有一些建议存在的目录,具体如下。

1.1.2 FHS建议根目录中应该存在的目录

目录应该存放的文件
/home一般用户的默认家目录(home directory),新建一般用户的时候,默认的家目录会被指定到这个目录下面。家目录的两种重要代号如下:
- ~:代表当前用户的家目录
- ~username:代表username这个用户的家目录
/lib<qual>用来存放于/lib不同格式的二进制函数库,比如支持64位应用的库函数通常位于/lib64目录中
/root系统管理员的家目录,当进入单用户模式进行系统维护的时候,仅需要挂载根目录时,该目录就能够拥有root的家目录,所以/root目录通常与根目录(/)放在同一个分区中

事实上,FHS对于Linux的目录配置相关的定义只有上述这些,不过,Linux上还有一些非常重要的目录需要了解:

目录应该存放的文件
/lost+found这个目录是ext2, ext3, ext4文件系统才会产生的一个目录,用于将文件系统发生错误时的一些遗失片段存放在这个目录中,如果使用xfs文件系统,则不存在该目录
/proc这个目录本身是一个虚拟文件系统(virtual filesystem),其中的数据实际上都是存在于内存中的,比如系统内核、进程信息(process)、外部设备的状态以及网络状态等。由于这个目录中的内容都是存在于内存中,所以并不像此前的目录一样占用磁盘空间
/sys于/proc类似,也是一个虚拟文件系统,主要记录内核于系统硬件信息相关的内容,包括目前已经加载内核模块以及内核检测到的硬件设备信息等,同样不占用磁盘空间

在早期的Linux系统发生问题时,恢复模式通常只挂载根目录,所以有5个重要的目录要求与根目录放置在相同的分区中:/etc, /bin, /sbin, /dev, /lib这5个目录。现在很多Linux发行版已经将很多不必要的文件移出了/usr目录,所以/usr目录也越来越精简。因为FHS标准对/usr目录的建议是“即便挂在为只读的方式,系统还是可以正常运行”,所以恢复模式也能同时挂载/usr目录。实际上CentOS 7.x发行版在恢复模式下就是这样的,其将/sbin/、/bin、/lib都移到了/usr目录下面

1.2 /usr目录的意义与内容

根据FHS标准的建议,/usr目录中存放的数据属于可共享、不可变的,即/usr中的数据通常可以通过NFS共享给其他主机来使用。usr实际上是Unix Software Resource的缩写,而不是user的缩写。FHS建议开发者将他们的数据合理的放置在这个目录下的合适的子目录中,而不是自行建立该软件自己独有的目录。

同样,该目录分为FHS要求必须存在的目录以及建议存在的目录两部分。

1.2.1 FHS要求/usr下必须存在的目录

具体如下表所示:

目录应该存放的文件
/usr/bin所有一般用户能够使用的命令都存放在这里,目前CentOS 7.x已经将全部的用户命令迁移至此目录,同时通过链接文件将根目录下的/bin目录指向这个目录。即/usr/bin与/bin目录的内容是相同的。另外,FHS标准要求该目录下不能存在子目录
/usr/lib与/lib的功能相同,所以/lib目录实际上就是链接到这个目录的
/usr/local系统管理员安装的软件,建议安装到这个目录中,以便于管理。/usr/local这个目录下也具有bin, etc, include, lib这些目录
/usr/sbin不是系统正常允许所需要的命令,通常是某些网络服务器软件的服务命令(daemon),不过基本与/sbin目录的内容差不多,所以根目录下的/sbin也是链接到这个目录上的
/usr/share用于放置只读的数据文件,这个目录下放置的数据基本是不分硬件架构均可读取的数据,因为基本都是文本文件。该目录下常见的子目录包括:
- /usr/share/man:在线帮助文档,man手册
- /usr/share/doc:软件的说明文件
- /usr/share/zoneinfo:时区文件

1.2.2 FHS建议/usr下可以存在的目录

/usr/目录下建议可以存在的目录包含下表中的内容:

目录应该存放的文件
/usr/games与游戏相关的数据文件
/usr/include用于存放c/c++等语言的头文件(*.h)以及包含文件(include),当以tar.gz的方式安装软件的时候,就需要用着其中的许多文件
/usr/libexec存放一些不被一般用户常用的执行文件或者脚本等,比如X窗口下的操作命令很多都是存放在这个目录中的
/usr/lib<qual>与/lib<qual>类似,实际上根目录下的/lib<qual>就是链接到这个目录上的
/usr/src用于存放源代码,src是source的缩写;内核源代码建议存放在**/usr/src/Linux/**这个目录下

/usr目录在安装的时候会占用较多的磁盘空间。

1.3 /var目录的意义与内容

1.3.1 FHS要求/var下必须存在的目录

与/usr目录不同,该目录会在系统运行后才开始逐渐占用磁盘空间。因为/var目录主要存放的是经常变动的文件,比如缓存(cache)、日志文件(log file)以及某些软件运行产生的数据文件等,比如程序文件(lock file、run file)或者MySQL的数据库文件等。常见的子目录如下表所示:

目录应该存放的文件
/var/cache应用程序运行过程中产生的一些缓存
/var/lib存储应用程序运行过程中所需要使用的数据文件,该目录下各个软件应该有各自的子目录,比如MySQL的数据库存放在/var/lib/mysql目录中,而rpm数据库则存放在/var/lib/rpm这个目录中
/var/lock用于存放锁文件,被上锁的文件同时只能被一个应用使用,以免多个应用同时使用该文件的时候造成读写信息不一致的情况
/var/log存放程序和系统的日志文件,比如/var/log/messages, /var/log/secure, /var/log/wtmp(记录登录信息)等
/var/mail存放个人邮箱的目录,链接到/var/spool/mail目录
/var/run用于存放某些程序启动之后的PID文件,与/run目录作用相同,实际上是链接到/run目录的
/var/spool通常用于存放队列数据,这些数据被使用后通常会被删除,比如系统接收到的新邮件会被放置到/var/spool/mail目录中,当用户首下该邮件之后,就会从/var/spool/mail中将该邮件删除;如果暂时寄不出去的邮件,则会被存放在/var/spool/mqueue这个目录中,等到被寄出之后再删除/var/spool/mqueue这个目录中的对应邮件。如果是计划任务相关数据(crontab),则会被放置到/var/spool/cron这个目录中

更详细的内容可以阅读FHS官方文档

1.3.2 基于FHS对比CentOS与SUSE以及Ubuntu的差异

FHS标准实际仅仅定义了顶层(/)以及次顶层(/usr以及/var)目录下应该存放的内容,所以在其他子目录层级内,就可以随开发者自行配置了。比如CentOS的网络配置文件就存放在/etc/sysconfig/network-scripts这个目录中;而SUSE则是存放在/etc/sysconfig/network这个目录中;Ubuntu则是存放在/etc/netplan(或者是/etc/network这个目录中)这个目录中。

目录树的结构图如下所示:
在这里插入图片描述

2. Linux文件系统中的文件元数据信息相关操作

所谓的元数据信息就是指文件中实际记录的内容之外,描述文件的基本特征的一些信息。Linux中文件的元数据信息包括文件大小(文件所占用的磁盘空间)、权限、链接数、修改日期、访问日期、inode数、uid/gid、文件类型等等,这些信息是与文件中实际记录的内容无关的。除了文件之外,目录也具有类似的元数据信息。要查看文件的元数据信息,主要是通过两个命令来实现:ls以及stat,这两个命令会以不同的形式输出文件的元数据信息。

2.1 ls命名查看文件的元数据信息

ls命令默认只会列出文件名,并不会列出其他元数据信息,为此,就需要给ls命令指定一些选项,以便能够列出文件的相关元数据信息,最常用的选项有如下几个:

  • -i:列出inode信息

    查看文件的inode信息,如下所示:

    ~]# ls -i file.txt 
    402846113 file.txt
    

    如上所述,文件file.txt的inode号为402846113,文件名为file.txt

  • -l:列出文件的详细信息,包括文件的权限、文件类型、链接数、uid/gid以及mtime(内容修改时间)等信息

    该选项的输出结果如下所示:

    ~]# ls -l file.txt
    -rw-r--r-- 1 root root 45 Feb 23 22:51 file.txt
    

    上述输出中各部分的内容分别如下所示:

    • -rw-r–r--:表示文件类型以及文件权限,第一个-表示file.txt为一个常规文件(regular file),如果是目录,则第一个-会变为d,如果是链接文件,则会变为l;后面的9个字符表示权限,3个为一组,从左到右分别代表读取、写入和执行的权限。比如第一组rw-代表该文件的所有者所具有的权限为可以读取和写入,但是不能作为命令或者脚本的形式执行该文件;第二组r–表示该文件的所属组所具有的权限,分别为具有读取权限,但是不具有写入和执行的权限;第三组r–代表除了该文件的所有者和所属组之外,系统上的其他账号的权限,分别为具有读取权限,但是不具有写入和执行的权限
    • 1:表示文件当前的链接数为1,当该链接数被减为0的时候,该文件就被删除了
    • root root:这两个表示文件的所有者和所属组,前面的为该文件的所有者的名字,后面的为该文件的所属组的名字
    • 45:表示文件的磁盘空间占用量,即文件大小,此处默认的显示单位是字节数,即bytes
    • Feb 23 22:51:表示文件内容最后一次修改的时间,即mtime
    • file.txt:表示该文件的名字
  • -d:仅列出目录本身,而不列出其中的内容

    当不关心该目录下包含什么内容,而只是想查看该目录自身的元数据信息的时候,就需要使用这个选项了。效果如下所示:

    ~]# ls -d .
    .
    ~]# ls -ld .
    dr-xr-x---. 27 root root 4096 Mar 21 10:13 .
    

    **注意:**该选项通常不单独使用,而是与-l选项一起使用

    上述输出中,列出的内容就是目录的元数据信息,第一列的第一个字母变为==d==,表示其是一个目录。剩下的内容与文件的解释相同,不再赘述。

  • -h:将文件的容量以人类易读的方式(比如GiB、KiB等单位)列出来

    该选项同样不能单独使用,如果单独使用,并没有什么效果;也需要与-l选项结合使用。具体输出结果如下所示:

    ~]# ls -lh kubectl_api-resources
    -rw-r--r-- 1 root root 8.4K Jan 28 15:21 kubectl_api-resources
    

    上述输出的==8.4K==这一列即文件的大小为8.4KiB。注意,如果文件的大小不足1KiB大小,则仍然会以字节数的形式进行显示,即不带任何单位的显示形式。如下所示:

    ~]# ls -lh file.txt 
    -rw-r--r-- 1 root root 45 Feb 23 22:51 file.txt
    

    上述输出的45就是表示文件的大小为45字节,查看下文件的内容如下所示:

    ~]# cat file.txt
    43 - 44 - 45
    spam
    [1, 2, 3]
    {'a': 1, 'b': 2}
    
  • -n:将uid和gid以数字的形式列出来,而不是以名字的形式列出来

    这个选项同样不能单独使用,需要与-l选项结合使用。其输出效果如下所示:

    ~]# ls -ln file.txt 
    -rw-r--r-- 1 0 0 45 Feb 23 22:51 file.txt
    

    上述输出的==0 0==就表示文件的所有者ID和所属组ID分别为0,对应于所有者root和所属组root

  • -r:将结果以反向排序的方式进行输出,默认是按照文件名从小到大的顺序进行排列

  • -R:连同当前目录的子目录中的内容一并输出,即递归输出当前目录下的所有内容

  • -t:以时间排序进行输出

  • --time=[atime|ctime]:指定以何种时间戳显示时间,默认是mtime,即文件内容的修改时间

    • ctime:表示文件状态最后一次修改的时间,比如文件的权限或者属性被更改的时候,都会修改这个时间

      该选项通常也是与-l选项结合使用,其输出结果如下所示:

      ~]# ls -l --time=ctime file.txt 
      -rw-r--r-- 1 root root 45 Feb 23 22:51 file.txt
      

      上述输出的时间戳部分==Feb 23 22:51==即表示该文件的状态修改时间

    • atime:表示文件的访问时间,每次读取文件内容的时候,都会更新该时间戳;比如使用cat命令读取文件内容时,就会更新该时间戳

      该选项的输出结果如下所示:

      ~]# ls -l --time=atime file.txt
      -rw-r--r-- 1 root root 45 Mar 21 10:11 file.txt
      

      上述输出的时间戳部分==Mar 21 10:11就表示该文件最后一次被访问的时间,比如上述通过cat命令查看文件内容的操作,就会更新该时间戳为执行cat file.txt命令时的系统时间。

除了可以使用ls命令查看文件或目录的元数据信息之外,还可以使用下面介绍的stat命令。

2.2 stat命令查看文件的元数据信息

stat命令的输出更加直接,同样可以查看文件和目录的元数据信息,具体如下所示:

  • 查看文件的元数据信息

    ~]# stat file.txt 
      File: file.txt
      Size: 45              Blocks: 8          IO Block: 4096   regular file
    Device: 10303h/66307d   Inode: 402846113   Links: 1
    Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
    Access: 2021-03-21 10:11:34.244082611 +0800
    Modify: 2021-02-23 22:51:41.924019809 +0800
    Change: 2021-02-23 22:51:41.924019809 +0800
     Birth: -
    

    上述输出中列出了文件file.txt的元数据信息,且都用key: value的形式标注了各个字段。其中包含了文件名(File)、文件大小(Size)、占的块大小(Blocks)、文件类型(regular file)、设备号、inode号、链接数(Links)、权限(Access)、所有者(Uid)、所属组(Gid)、访问日期(Access)、文件内容修改日期(Modify)、文件权限或属性等元数据修改日期。

  • 查看目录的元数据信息

    stat命令查看目录的元数据信息的时候,无需额外的选项支持,具体如下所示:

    ~]# stat .
      File: .
      Size: 4096            Blocks: 8          IO Block: 4096   directory
    Device: 10303h/66307d   Inode: 402653313   Links: 27
    Access: (0550/dr-xr-x---)  Uid: (    0/    root)   Gid: (    0/    root)
    Access: 2021-03-21 11:52:26.855540723 +0800
    Modify: 2021-03-21 11:51:53.625359681 +0800
    Change: 2021-03-21 11:51:53.625359681 +0800
     Birth: -
    

    输出的字段内容与文件的输出结果基本中包含的字段信息基本一致。

3. Linux文件系统的符号链接与硬链接

Linux文件系统的链接主要有两种,分别为:符号链接(symbolic link)硬链接(hard link),前者类似于Windows系统的快捷方式,可以连接到文件或者目录上;后者是通过文件系统的inode链接产生新的文件名,而不是产生新的文件。

3.1 硬链接(hard link)

由于文件是存储在文件系统中的,所以每个文件在创建的时候都会被分配一个inode,来指向文件内容实际存储的块(block)。所以要读取该文件的内容,就需要通过目录记录的文件名指向到正确的inode号码,通过inode号码定位到文件内容所在的区块。换句话说,文件名只与该文件所在的目录相关,而文件内容则与inode号相关。前面提到硬链接不产生新文件,而是通过inode链接产生新的文件名的方式形成到原始文件的链接。其实质就是有多个文件名指向同一个inode号,而文件名是与inode不相关的,而是与目录相关的,所以创建一个新的文件名就是在目录中创建一个新的名字而已,并不会产生新的inode,所以也就不会产生新的文件了。

通常来说,使用硬链接设置文件链接的时候,磁盘空间与inode数都不会发生改变,硬链接只是在某个目录的区块内多写入一个关联数据(指向被链接文件inode号的文件名),所以并不会增加inode数也不会增加磁盘区块数量的消耗。

注意:如果目录区块中增加的指向被链接文件的inode号的文件名刚好填满目录的区块时,就可能为目录增加一个新的区块记录文件名与inode的关联,从而导致磁盘空间的变化,但是硬链接的文件名用掉的数据量很小,所以通常不会改变inode与磁盘空间的大小。

硬链接不应该跨越文件系统而存在,只能存在于单一文件系统中,所以硬链接是有限制的:

  • 不能跨越文件系统
  • 不能链接目录

那么应该如何创建文件的硬链接呢?可以通过两个命令来完成:

  • cp -l

    该命令的使用形式为:cp -l /path/to/target_file /path/to/hard_link_file

    下面还是以file.txt这个文件为例进行演示,在创建硬链接之前,file.txt的元数据信息如下所示:

    ~]# ls -li file.txt 
    402846113 -rw-r--r-- 1 root root 45 Feb 23 22:51 file.txt
    

    上述输出中,需要关注的是第一列的inode号==402846113,以及第三列的链接数1==。此时创建一个新的硬链接,具体如下所示:

    ~]# cp -l ${PWD}/file.txt file_hardlink.txt
    ~]# ls -li file*
    402846113 -rw-r--r-- 2 root root 45 Feb 23 22:51 file_hardlink.txt
    402846113 -rw-r--r-- 2 root root 45 Feb 23 22:51 file.txt
    

    上述第一行在当前目录创建了一个名为file_hardlink.txt的硬链接,第二行查看这两个文件的元数据信息,其中可以看出,这两个文件的inode号都为==402846113,但是链接数从此前的1变成了2==,因为增加了一个硬链接。文件的其他属性也没有发生改变。

  • ln

    该命令创建硬链接时的使用形式为:ln /path/to/target_file /path/to/hard_link_file

    下面仍然以file.txt文件为例进行演示,具体如下所示:

    ~]# ln ${PWD}/file.txt file_hardlink_1.txt
    ~]# ls -li file*
    402846113 -rw-r--r-- 3 root root 45 Feb 23 22:51 file_hardlink_1.txt
    402846113 -rw-r--r-- 3 root root 45 Feb 23 22:51 file_hardlink.txt
    402846113 -rw-r--r-- 3 root root 45 Feb 23 22:51 file.txt
    

    上述代码框的第一行中创建了第二个名为file_hardlink_1.txt的硬链接,第二行列出了此时的目标文件以及2个硬链接的元数据信息,同样第一列的inode号402846113并没有发生改变,改变的只是第三列的链接数,从上一步操作的2变为了=,因为此时包括2个硬链接在内一共有3个链接指向相同的inode号。

使用硬链接的好处就是在不增加磁盘空间占用的情况下,可以增加文件的冗余,即便此时将原始的target文件file.txt删除,此时仍然可以通过创建的2个硬链接访问到文件内容。增加了文件的安全性。只要该inode号402846113的链接数不为0,那么该文件就不会从文件系统中被删除。具体如下所示:

查看file.txt文件的内容,然后删除原始的file.txt文件,然后查看两个链接文件的状态,具体如下所示:

~]# cat file.txt 
43 - 44 - 45
spam
[1, 2, 3]
{'a': 1, 'b': 2}
~]# rm -f file.txt
~]# ls -li file*
402846113 -rw-r--r-- 2 root root 45 Feb 23 22:51 file_hardlink_1.txt
402846113 -rw-r--r-- 2 root root 45 Feb 23 22:51 file_hardlink.txt

上述代码框中,第一行查看了文件内容,第6行删除了原始的target文件file.txt,第7行查看了两个硬链接的元数据信息,inode号并没有发生改变,改变的只是链接数,从此前的3变为了此处的2,表示此时还有2个硬链接指向402846113这个inode号。此时应该仍然是可以访问该文件中的内容的,具体如下所示:

~]# cat file_hardlink.txt 
43 - 44 - 45
spam
[1, 2, 3]
{'a': 1, 'b': 2}
~]# cat file_hardlink_1.txt 
43 - 44 - 45
spam
[1, 2, 3]
{'a': 1, 'b': 2}

此时可以看出,仍然可以通过这两个硬链接查看inode号402846113所指向的区块中记录的内容。因为inode并没有被删除,文件名与inode的关联也仍然存在,所以自然能够通过这两个硬链接查看原始的文件内容了。

3.2 符号链接(symbolic link)

不同于硬链接是在目录中创建一个文件名指向目标文件的inode号,符号链接则是创建一个独立的文件,使这个文件指向它链接的那个文件的文件名。由于只是利用文件来作为指向的操作,所以当目标文件被删除之后,符号链接就无法被打开了,其实就是找不到原始文件名了,因为原始文件名与其对应的inode之间的链接关系被打破了,文件内容仍然存在于对应的区块中,只是由于没有到该inode的链接关系,所以无法访问目标区块中存储的内容了。当符号链接的目标文件存在的时候,既可以通过目标文件访问inode指向的区块中存储的文件内容,也可以通过符号链接访问该inode指向的区块中存储的文件内容。同时,由于符号链接并不是连接到目标文件的inode号,只是创建了一个新的文件,并将这个文件指向了目标文件所在目录的文件名,所以符号链接通常比目标文件小。另外,由于符号链接所建立的文件是一个独立的新文件,所以会占用inode资源和实际的存储区块。符号链接除了可以链接到文件之外,还可以链接到目录上。

创建符号链接的两种方式与创建硬链接的方式类似,具体如下所示:

  • cp -s

    此处仍然以file.txt这个文件为例,将此前的一个硬链接重命名为file.txt,然后删除多余的硬链接。具体如下所示:

    ~]# ls -li file*
    402846113 -rw-r--r-- 2 root root 45 Feb 23 22:51 file_hardlink_1.txt
    402846113 -rw-r--r-- 2 root root 45 Feb 23 22:51 file_hardlink.txt
    ~]# mv file_hardlink.txt file.txt
    ~]# rm file_hardlink_1.txt 
    rm: remove regular file 'file_hardlink_1.txt'? y
    ~]# ls -li file.txt 
    402846113 -rw-r--r-- 1 root root 45 Feb 23 22:51 file.txt
    

    此时文件的链接数恢复为1,多余的硬链接已经被删除了。

    接下来创建符号链接,具体如下所示:

    ~]# cp -s ${PWD}/file.txt file_symbolic_link.txt
    ~]# ls -li file*
    403799665 lrwxrwxrwx 1 root root 14 Mar 21 13:46 file_symbolic_link.txt -> /root/file.txt
    402846113 -rw-r--r-- 1 root root 45 Feb 23 22:51 file.txt
    

    上述代码框的第一行创建了一个符号链接,第二行查看创建的符号链接以及原始的目标文件的元数据信息,从上述输出中可见,符号链接文件的inode号与原始的目标文件的inode号并不相同,可见是在文件系统上创建了一个新的文件,且其第二列的文件类型以及权限部分标识的文件类型为l,即该文件为一个链接文件,第文件大小部分,显示该链接文件的大小为14个字节,由于上述第三行输出的箭头后面的原始目标文件为/root/file.txt刚好14个字符,每个字符占用1个字节,所以链接文件的大小即为14个字节。

    注意:这里的大小是指原始的目标文件包含完整路径的字符个数,每个字符占用1个字节。

    在原始的目标文件存在的时候,既可以通过访问目标文件的方式读取其中的内容,也可以通过符号链接的方式访问进行访问,具体如下所示:

    ~]# cat file.txt 
    43 - 44 - 45
    spam
    [1, 2, 3]
    {'a': 1, 'b': 2}
    ~]# cat file_symbolic_link.txt 
    43 - 44 - 45
    spam
    [1, 2, 3]
    {'a': 1, 'b': 2}
    

    备份下原始的目标文件,然后删除file.txt文件,此时就无法再通过前面的符号链接访问目标文件中存储的内容了。因为在目录中已经不存在符号链接所指向的文件名了。具体如下所示:

    ~]# cp file.txt file.txt.bak
    ~]# rm -f file.txt
    ~]# cat file_symbolic_link.txt 
    cat: file_symbolic_link.txt: No such file or directory
    ~]# ls -li file_symbolic_link.txt
    403799665 lrwxrwxrwx 1 root root 14 Mar 21 13:46 file_symbolic_link.txt -> /root/file.txt
    

    上述代码框中,虽然符号链接仍然存在,但是其所指向的目标文件/root/file.txt已经被删除了,所以此时就无法通过符号链接访问原始目标文件中存储的内容了,但是仍然可以通过备份的原始目标文件访问。具体如下所示:

    ~]# cat file.txt.bak 
    43 - 44 - 45
    spam
    [1, 2, 3]
    {'a': 1, 'b': 2}
    

    上述操作由于没有指定创建硬链接,所以造成了备份的目标文件的inode号发生了改变,原始的file.txt的inode号为402846113=,cp命令复制之后的新文件的inode号为403234376,具体如下所示:

    ~]# ls -li file*
    403799665 lrwxrwxrwx 1 root root 14 Mar 21 13:46 file_symbolic_link.txt -> /root/file.txt
    403234376 -rw-r--r-- 1 root root 45 Mar 21 13:58 file.txt.bak
    

    说明cp命令如果不指定-l选项,则会使用新的inode号创建新的文件,同时该inode号指向原始的区块。

  • ln -s

    下面使用file.txt.bak这个文件作为原始的目标文件,通过ln -s命令创建符号链接文件。具体如下所示:

    ~]# ls -li file*
    403799665 lrwxrwxrwx 1 root root 14 Mar 21 13:46 file_symbolic_link.txt -> /root/file.txt
    403799666 lrwxrwxrwx 1 root root 18 Mar 21 14:15 file_symbolic_link.txt.bak -> /root/file.txt.bak
    403234376 -rw-r--r-- 1 root root 45 Mar 21 13:58 file.txt.bak
    

    上述代码框中显示了新的符号链接文件的inode号为403799666,文件的大小为18字节,因为此时符号链接所指向的目标文件的全路径文件名为18个字符组成,每个字符占用1个字节,所以此时符号链接文件的大小就为18个字节。

    同理,由于此时原始的目标文件存在,所以仍然可以通过原始的目标文件和符号链接文件访问记录在原始区块中的文件内容。具体如下所示:

    ~]# cat file.txt.bak 
    43 - 44 - 45
    spam
    [1, 2, 3]
    {'a': 1, 'b': 2}
    ~]# cat file_symbolic_link.txt.bak 
    43 - 44 - 45
    spam
    [1, 2, 3]
    {'a': 1, 'b': 2}
    

    如果此时删掉原始的目标文件file.txt.bak这个文件,那么就无法通过符号链接文件访问该文件的内容了,具体如下所示:

    ~]# rm -f file.txt.bak 
    ~]# cat file_symbolic_link.txt.bak 
    cat: file_symbolic_link.txt.bak: No such file or directory
    ~]# ls -li file*
    403799665 lrwxrwxrwx 1 root root 14 Mar 21 13:46 file_symbolic_link.txt -> /root/file.txt
    403799666 lrwxrwxrwx 1 root root 18 Mar 21 14:15 file_symbolic_link.txt.bak -> /root/file.txt.bak
    

    此时就提示没有此文件或目录,因为符号链接指向的目标文件已经被删除了,所以自然无法通过目标文件名访问到原始区块中记录的内容了。

不过由于硬链接在使用上的限制,无法链接目录,且无法跨越文件系统,所以在实际使用上,符号链接使用的更加广泛一些。

由于符号链接是通过创建一个新的文件,并且将该文件连接到目标文件的文件名上,所以操作符号链接的时候,实际上操作的就是原始文件或者目录。比如创建了一个目录的符号链接/root/bin,并将这个符号链接文件指向/bin这个目录,当删除/root/bin中内容的时候,实际上删除的就是原始的目标路径/bin中的内容。所以使用上千万注意。

3.3 关于目录的链接数量

当在当前目录中创建一个新目录的时候,实际上并不会增加inode数(只要该目录命没有填满此目录当前的inode号所指定的区块,就不会创建新的inode以及区块)。一个空目录实际包含三部分内容,具体如下所示:

  • /root/test_link - 当前目录
  • /root/test_link/. - .同样表示当前目录
  • /root/test_link/.. - ..表示当前目录的父目录

下面分别以上述的三部分分别查看目录的元数据信息,可以看出,虽然创建了新的目录名,但是当前目录以及当前目录的父目录的inode号相同,即并没有因为创建了新的目录而增加inode号。具体如下所示:

~]# ls -lid .
402653313 dr-xr-x---. 27 root root 4096 Mar 21 14:43 .
~]# ls -lid ${PWD}
402653313 dr-xr-x---. 27 root root 4096 Mar 21 14:43 /root
~]# mkdir test_link
~]# ls -lid ${PWD}/test_link
402846113 drwxr-xr-x 2 root root 6 Mar 21 14:43 /root/test_link
~]# ls -lid ${PWD}/test_link/.
402846113 drwxr-xr-x 2 root root 6 Mar 21 14:43 /root/test_link/.
~]# ls -lid ${PWD}/test_link/..
402653313 dr-xr-x---. 28 root root 4096 Mar 21 14:43 /root/test_link/..
~]# ls -lid ${PWD}
402653313 dr-xr-x---. 28 root root 4096 Mar 21 14:43 /root
~]# ls -lid test_link
402846113 drwxr-xr-x 2 root root 6 Mar 21 14:43 test_link

从上述输出也映射了此前的说法,即在目录中创建文件名或者目录名的时候,包含该文件或者目录的父目录并没有增加inode数。

上述新创建的目录中,虽然是新创建的空目录,没有创建任何其他链接,但是其链接数并不是1,不同于新创建的文件,链接数为1,此处新目录的链接数是2,因为除了当前目录命之外,在当前目录下还有一个.,表示当前目录,所以也是链接到test_link这个目录上的。

当创建了新空目录之后,该新目录的链接数为2,而新创建目录的父目录则会增加1,在上述的代码框中也确实是从27增加到了28个链接。

4. 常用的文件管理命令

Linux系统的文件是以目录的形式组织起来的,且一切皆文件,而伴随着文件和目录存在的一个很重要的存在,就是权限。这部分将会介绍文件和目录的基本管理命令、文件内容查看命令、文件与目录的权限管理命令、以及命令与文件查找相关的命令。

4.1 文件和目录的基本管理命令

Linux上目录相关的特殊符号和环境变量可以增加工作效率,主要有以下几个:

  • . - 代表当前目录
  • .. - 代表当前目录的父目录
  • - - 代表上一个使用的工作目录
  • ~ - 代表当前用户的家目录,等效于环境变量HOME
  • ~username - 代表username这个用户的家目录

在所有目录下面都会存在的两个目录就是.和..这两个目录。此外还有一个很重要的环境变量:

  • PWD:表示当前工作目录

Linux文件系统中的路径分为绝对路径相对路径,其中:

  • 绝对路径:表示方式为根目录(/)开头,比如/root, /usr/local等写法都是绝对路径写法
  • 相对路径:表示方式为.或者..开头,而不是以根目录(/)开头,相对路径的含义是相对于当前的工作目录的路径,比如cd ../man表示切换到当前路径的父目录中的man这个目录中;cd ./local表示切换到当前目录下的local这个目录中,当然,这种写法也可以直接写成cd local,即表示切换到当前路径下的local这个目录中,即切换到当前目录下的子目录中,无需使用./这种方式。

在Linux系统的日常使用中,常见的处理文件和目录的命令有如下几个:

  • cd:用于切换工作目录

    该命令的使用形式为cd dirname,常用的使用形式如下:

    • 从其他目录切换回当前用户的家目录:

      ~]# cd /tmp/
      tmp]# cd ~
      ~]#
      
    • 切换到某个用户的家目录下:

      ~]# cd /tmp
      tmp]# cd ~root
      ~]# 
      
    • 切换到此前的工作目录中:

      ~]# cd -
      /tmp
      tmp]#
      
    • 相对路径的方式切换到上一层目录中

      tmp]# cd ..
       /]#
      
    • 绝对路径的方式切换路径,并通过环境变量PWD显示当前路径

      /]# cd /usr/local
      local]# echo $PWD
      /usr/local
      local]# 
      
  • pwd:显示当前工作目录

    该命令的使用形式为:pwd [-P]其中-P选项可以省略,其含义为显示真正的路径,而不是使用符号链接路径。该命令的使用示例如下:

    • 显示当前路径

      local]# pwd
      /usr/local
      
    • 显示目录符号链接的原本路径

      local]# cd /bin
      bin]# pwd
      /bin
      bin]# pwd -P
      /usr/bin
      

      上述代码框中,直接使用pwd命令的时候,表示打印当前的工作路径,加上-P选项之后,表示打印其实际路径,而不是链接路径,由于/bin是链接到/usr/bin这个目录上的,所以当使用-P选项之后,打印的就是/bin目录的原始目标路径。可以通过ls命令查看/bin目录的链接关系,如下:

      bin]# ls -ld /bin
      lrwxrwxrwx 1 root root 7 Nov  3 23:22 /bin -> usr/bin
      

      上述输出显示/bin为符号链接,其链接的原始目标路径名称为usr/bin

  • mkdir:创建新目录

    该命令的使用形式为:mkdir [-mp] dir_name,中括号中的选项可以省略,省略的时候,只能创建一级目录,其选项解释如下:

    • -m:创建目录的同时指定目录的权限,而不是使用默认的权限(umask)
    • -p:递归创建多级目录

    该条命令的使用示例如下:

    • 在当前目录创建一个新目录

      tmp]# mkdir test_mkdir
      tmp]# cd test_mkdir
      test_mkdir]# pwd
      /tmp/test_mkdir
      test_mkdir]#
      

      上述代码框中显示了单级目录创建过程

    • 创建目录的同时指定目录的权限

      test_mkdir]# mkdir -m 711 test
      test_mkdir]# ls -ld test
      drwx--x--x 2 root root 6 Mar 21 15:52 test
      

      上述目录在创建的时候通过-m 711选项指定了新目录的权限

    • 递归创建多级目录

      test_mkdir]# mkdir test1/test2/test3
      mkdir: cannot create directory ‘test1/test2/test3’: No such file or directory
      test_mkdir]# mkdir -p test1/test2/test3
      test_mkdir]# cd test1/test2/test3
      test3]# pwd
      /tmp/test_mkdir/test1/test2/test3
      test3]#
      

      上述输出中可以看出,如果不使用-p选项是无法递归创建多级目录的。该命令的变种形式,可以结合花括号展开,创建多级目录,具体如下所示:

      test_mkdir]# ls 
      test  test1
      test_mkdir]# ls test
      test_mkdir]# mkdir -p test/test{1,2}/test3/test4
      test_mkdir]# tree .
      .
      ├── test
      │   ├── test1
      │   │   └── test3
      │   │       └── test4
      │   └── test2
      │       └── test3
      │           └── test4
      └── test1
          └── test2
              └── test3
      
      10 directories, 0 files
      test_mkdir]# tree test
      test
      ├── test1
      │   └── test3
      │       └── test4
      └── test2
          └── test3
              └── test4
      
      6 directories, 0 files
      

      上述命令通过test/test{1,2}/test3/test4表示在test目录下创建两个目录test1和test2,然后分别在这两个目录下递归创建test3和test4目录。

      通过tree命令可以查看创建的目录结构。

  • rmdir:删除一个空目录

    该命令的使用形式为:rmdir [-p] dir_name,选项-p表示连同上一级目录一起删除。需要注意的是,该命令只能删除空目录,如果其中包含文件则无法删除。该命令的使用示例如下所示:

    • 删除单一空目录

      test2]# ls 
      test3
      test2]# ls test3/
      test2]# rmdir test3/
      test2]# ls 
      test2]#
      

      上述输出中,由于test3目录为空目录,所以可以使用这个命令直接删除。

      如果被删除的目录中有其他目录或者文件,则无法直接通过这个命令进行删除,具体如下所示:

      test_mkdir]# ls -R test1/
      test1/:
      test2
      
      test1/test2:
      test_mkdir]# 
      test_mkdir]# rmdir test1
      rmdir: failed to remove 'test1': Directory not empty
      

      上述表示test1目录下包含一个空目录test2,ls -R表示递归列出该目录下的所有目录的内容。此时由于test1目录不为空目录,所以无法通过这个命令直接删除。

    • 递归删除多级空目录

      test_mkdir]# ls test
      test/  test1/ 
      test_mkdir]# rmdir -p test1
      rmdir: failed to remove 'test1': Directory not empt
      test_mkdir]# rmdir -p test1/test2/
      test_mkdir]# ls 
      test
      

      因为-p选项的含义是递归删除该目录以及该目录的父目录,所以第三行的执行方式报错,第5行的执行方式可以正常执行。

    实际上,该命令在日常的管理和使用中并不常用。

  • ls:列出当前目录或者文件

    这个命令在前面的2.1小节做了详细介绍,此处不再展开。具体使用可参见man ls的帮助信息。

  • cp:复制粘贴目录或者文件

    该命令的使用形式分为2种形式:

    • 文件拷贝为文件:cp [-adfilprsu] source destination
    • 文件拷贝到目录:cp [options] source1 source2 source3 ... directory

    cp命令前面的符号链接和硬链接部分介绍了两个选项-l-s,分别用于创建硬链接和符号链接。该命令的其他选项解释如下:

    • -a:相当于选项-dr --preserve=all,表示递归拷贝,同时保留所有属性与权限
    • -d:如果source为链接文件,则复制链接文件而不是链接文件指向的目标文件
    • -f:force的意思,即强制,如果destination已经存在,则删除之后再次重试
    • -i:如果destination已经存在,覆盖写入时会首先询问操作是否继续进行
    • -p:连同文件的属性(权限、用户、时间)一同复制,而不是用默认属性进行复制(备份的时候常用)
    • -r:递归复制,用于目录的复制操作(常用)
    • -u:当destination比source旧的时候才更新destination,或者destination不存在的时候才复制
    • -n:不覆盖写入已经存在的文件,会忽略掉-i选项
    • –preserve=all:除了-p的属性相关参数之外,还加入了SELinux属性,links以及xattr等也会被复制

    注意,如果source中有多个文件,那么destination必须是目录才可以。

    该命令的使用示例如下所示:

    • 单一source文件复制

      test_mkdir]# ls -l /etc/passwd
      -rw-r--r-- 1 root root 3572 Dec  1 22:47 /etc/passwd
      test_mkdir]# cp /etc/passwd .
      test_mkdir]# ls -l ./passwd
      -rw-r--r-- 1 root root 3572 Mar 21 16:36 ./passwd
      
    • 多个source文件复制到目录中

      test_mkdir]# ls 
      kubectl_api-resources  passwd  test  tmp-i08.xpi
      test_mkdir]# cp kubectl_api-resources passwd tmp-i08.xpi test
      test_mkdir]# ls test/
      kubectl_api-resources  passwd  test1  test2  tmp-i08.xpi
      

      上述的source如果包含多个文件,那么目标必须是目录。

    • 复制目录

      test_mkdir]# ls /usr/share/doc/man-db/
      ChangeLog  man-db-manual.ps  man-db-manual.txt  NEWS  README
      test_mkdir]# ls 
      kubectl_api-resources  passwd  test  tmp-i08.xpi
      test_mkdir]# cp -arf /usr/share/doc/man-db/ test_cp
      test_mkdir]# ls test_cp/
      ChangeLog  man-db-manual.ps  man-db-manual.txt  NEWS  README
      

    由于cp命令可以拷贝文件的属性、权限等特性,所以在复制的时候,需要清楚的了解:

    • 是否需要完整的保留源文件的属性信息
    • 源文件是否为符号链接文件
    • 源文件是否为特殊文件,比如管道文件(FIFO)、套接字文件(socket)等
    • 源文件是否为目录等等
  • rm:删除目录或者文件

    该命令的使用形式为:rm [-fir] file/directory,该命令既可以删除文件也可以删除目录,且不要求目录为空目录。选项解释如下:

    • -f:force的意思,忽略不存在的文件以及参数,不会给出警告信息
    • -i:每次删除操作都会询问是否继续当前的删除操作
    • -r:递归删除,最常用于目录删除,在使用该选项之前一定要确认被删除的目录中不包含有用的数据

    该命令的使用示例如下所示:

    • 删除文件

      test_mkdir]# ls -l 
      total 1436
      -rw-r--r--  1 root root    8588 Mar 21 16:21 kubectl_api-resources
      -rw-r--r--  1 root root    3572 Mar 21 16:36 passwd
      drwx--x--x  4 root root      94 Mar 21 16:40 test
      drwxr-xr-x. 2 root root      98 Oct  3 18:22 test_cp
      -rw-r--r--  1 root root 1450752 Mar 21 16:38 tmp-i08.xpi
      test_mkdir]# rm passwd 
      rm: remove regular file 'passwd'? y
      test_mkdir]# rm -f tmp-i08.xpi 
      test_mkdir]#
      

      上述的删除操作中,如果不指定任何选项,则会进行提示,因为cp命令和rm命令都是调用的系统设置的别名,这两个命令的别名信息如下所示:

      • cp命令的别名:

        test_mkdir]# alias cp
        alias cp='cp -i'
        
      • rm命令的别名:

        test_mkdir]# alias rm
        alias rm='rm -i'
        

      alias命令既可以查看命令别名,也可以用于设置命令别名。上述的输出中显示,这两个命令别名默认包含了-i选项,即每次操作的时候都会进行提示。

    • 删除目录

      test_mkdir]# ls 
      kubectl_api-resources  test  test_cp
      test_mkdir]# rm -rf test_cp/
      test_mkdir]# ls 
      kubectl_api-resources  test
      

      删除目录的时候需要指定-r选项,表示递归删除目录下的内容;-f选项表示删除的时候不进行提示,且忽略不存在的文件以及参数。

    • 不调用命令别名,直接调用rm命令本身

      test_mkdir]# ls 
      kubectl_api-resources  test
      test_mkdir]# \rm kubectl_api-resources 
      test_mkdir]# ls 
      test
      

      上述的\rm表示不使用rm命令的别名形式,而是直接调用最原始的rm命令,及不包含任何预设选项的原始命令。

    • 删除以==-==开头的文件

      test_mkdir]# touch -aaa-
      touch: invalid option -- '-'
      Try 'touch --help' for more information.
      test_mkdir]# touch ./-aaa-
      test_mkdir]# ls -l
      total 0
      -rw-r--r-- 1 root root  0 Mar 21 17:02 -aaa-
      drwx--x--x 4 root root 94 Mar 21 16:40 test
      test_mkdir]# rm ./-aaa-
      rm: remove regular empty file './-aaa-'? y
      test_mkdir]# touch ./-aaa-
      test_mkdir]# rm -aaa-
      rm: invalid option -- 'a'
      Try 'rm ./-aaa-' to remove the file '-aaa-'.
      Try 'rm --help' for more information.
      test_mkdir]# rm -- -aaa-
      rm: remove regular empty file '-aaa-'? y
      test_mkdir]# 
      

      上述通过touch命令创建了一个以-开头的文件,对于文件名以-开头的文件,如果要执行删除操作,主要有两种方式,第一种是上述的第9行rm ./-aaa-的形式;第二种形式是上述的第16行rm -- -aaa-,通过====表示出现在其后的-都不作该命令的选项处理,只当作文件名处理。

      更像的用法,参见man rm的帮助手册。

  • mv:剪切粘贴目录或者文件,或者重命名文件和目录

    该命令的使用形式类似于cp命令,有两种形式:

    • 移动文件的位置或重命名文件:mv [-fiu] source destination
    • 移动文件或者目录:mv [options] source1 source2 source3 ... directory/

    该命令的选项参数解释如下:

    • -f:为force的意思,即强制,如果目标文件已经存在,则直接进行覆盖
    • -i:如果目标文件已经存在,则询问是否进行覆盖
    • -u:如果目标文件已经存在,且source比destination新的时候,才会移动文件进行更新,否则不会移动文件

    该命令的使用示例如下:

    • 移动文件位置或者重命名文件:

      test_mkdir]# mv /tmp/tmp-i08.xpi .
      test_mkdir]# ls 
      test  tmp-i08.xpi
      

      上述操作移动了一个文件,相当于改变了文件的存储位置。

      重命名上述移动过来的文件,具体如下所示:

      test_mkdir]# mv tmp-i08.xpi tmp.xpi
      test_mkdir]# ls 
      test  tmp.xpi
      

      上述就将当前目录下的文件重命名为tmp.xpi了。

    • 移动多个文件或者目录

      test_mkdir]# mv /tmp/tracker-extract-files.0/ /tmp/systemd-private-686b491b06734ef185be4c9d0731359d-bluetooth.service-V82blG/ .
      test_mkdir]# ls 
      systemd-private-686b491b06734ef185be4c9d0731359d-bluetooth.service-V82blG  test  tmp.xpi  tracker-extract-files.0
      test_mkdir]# 
      

      当源文件为多个文件或者目录的时候,目标必须为目录。

    -u选项的作用并不太明晰,具体如下所示:

    test_mkdir]# cat test_file                    
    123                                                         
    test_mkdir]# cat /tmp/test_file.bak            
    123    
    test_mkdir]# cat /tmp/test_file                
    123                                                         
    456                             
    

    上述当前工作目录下的test_file的内容为123,/tmp目录下的两个文件的内容分别为入第4行和第6、7行的输出。即/tmp/test_file的内容比当前目录下的test_file的内容新。但是实际加上-u选项移动的时候,即便是/tmp/test_file.bak这个跟当前目录下的test_file文件内容一致的文件也会被移动过来,并没有像该选项的描述那样,不比目标文件更新的话就不会进行移动操作。具体如下所示:

    [root@amdhost test_mkdir]# mv -u /tmp/test_file.bak ./test_file 
    mv: overwrite './test_file'? y                              
    [root@amdhost test_mkdir]# ls /tmp/ 
    test_file test_mkdir tmpaddon tmp-new.xpi 
    

    上述的/tmp/test_file.bak文件已经不存在了,因为已经被移动到当前工作目录中了。

  • basename:提取一个文件的完整路径中最后的文件名

    test_mkdir]# basename ${PWD}/test_file
    test_file
    test_mkdir]# echo ${PWD}/test_file
    /tmp/test_mkdir/test_file
    

    该命令可以获取一个完整路径文件的文件名。

  • dirname:提取一个文件或者目录的完整路径中的目录名称部分

    test_mkdir]# echo ${PWD}/test_file
    /tmp/test_mkdir/test_file
    test_mkdir]# dirname ${PWD}/test_file
    /tmp/test_mkdir
    

    该命令可以获取一个完整路径文件的路径名称。

4.2 文件内容查看命令

该部分内容涉及到的命令主要有:

  • cat:从第一行开始显示文件的内容

    该命令的使用形式为:cat [-AbEnTv] file,其中file可以省略,当省略或者file为==-==的时候,表示读取标准输入的内容。各个选项参数的解释如下所示:

    • -A:相当于-vET的组合选项,可以列出一些特殊字符,而不只是空白符
    • -b:列出行号,空白行不标注行号,只对非空白行进行行号显示
    • -E:将结尾的$换行符显示出来
    • -n:打印行号,空白行也会被标注行号
    • -T:将[tab]按键以^I的形式显示出来
    • -v:列出一些看不出来的特殊字符

    该命令的使用示例如下所示:

    • 直接读取文件的内容,并显示在标准输出中

      test_mkdir]# cat /etc/issue
      \S
      Kernel \r on an \m
      
      test_mkdir]#
      
    • 显示文件的非空白行的行号

      test_mkdir]# cat -b /etc/issue
           1  \S
           2  Kernel \r on an \m
      
      test_mkdir]# 
      
    • 显示空白行的行号

      test_mkdir]# cat -n /etc/issue
           1  \S
           2  Kernel \r on an \m
           3
      test_mkdir]# 
      
    • 显示特殊字符,而不仅仅是空白符

      test_mkdir]# cat -A /etc/man_db.conf
      #$
      # There are three mappings allowed in this file:$
      # --------------------------------------------------------$
      # MANDATORY_MANPATH^I^I^Imanpath_element$
      # MANPATH_MAP^I^Ipath_element^Imanpath_element$
      # MANDB_MAP^I^Iglobal_manpath^I[relative_catpath]$
      #---------------------------------------------------------$
      # every automatically generated MANPATH includes these fields$
      #$
      #MANDATORY_MANPATH ^I^I^I/usr/src/pvm3/man$
      #$
      MANDATORY_MANPATH^I^I^I/usr/man$
      MANDATORY_MANPATH^I^I^I/usr/share/man$
      MANDATORY_MANPATH^I^I^I/usr/local/share/man$
      #---------------------------------------------------------$
      
  • tac:从最后一行开始显示文件的内容,这个命令也是cat命令倒过来写的形式

    该命令的使用形式较简单,类似于cat命令,示例如下:

    test_mkdir]# tac /etc/issue
    
    Kernel \r on an \m
    \S
    test_mkdir]# 
    

    该命令很少被用到。

  • nl:带行号显示文件的内容

    该命令的使用形式为:nl [-bnw] file,选项解释如下所示:

    • -b:指定是否对空白行进行行号显示
      • -b a:表示无论是否为空行,均显示行号,类似于cat -n的效果
      • -b t:表示只对非空白行显示行号,空白行不显示行号,此为默认动作,效果类似于cat -b
    • -n:行号的显示位置,主要有3种
      • -n ln:行号显示在屏幕的最左边
      • -n rn:行号在栏位的最右边显示,且不加0
      • -n rz:行号在栏位的最右边显示,且加0
    • -w:行号栏位占用的字符数

    该命令的使用示例如下所示:

    • 默认的行号显示行为,即空白行不显示行号

      test_mkdir]# nl /etc/issue
           1  \S
           2  Kernel \r on an \m
             
      test_mkdir]# 
      

      上述命令等效于nl -b t /etc/issue

    • 显示空白行的行号

      test_mkdir]# nl -b a /etc/issue
           1  \S
           2  Kernel \r on an \m
           3
      test_mkdir]#
      
    • 空白行显示行号,且在栏位的最左边,并且补0

      test_mkdir]# nl -b a -n rz /etc/issue
      000001  \S
      000002  Kernel \r on an \m
      000003
      test_mkdir]#
      

      行号的默认位数为6位。

    • 将行号的位数限定为3位

      test_mkdir]# nl -b a -n rz -w 3 /etc/issue
      001     \S
      002     Kernel \r on an \m
      003
      test_mkdir]#
      

      上述命令通过-w 3选项将行号栏位数限定为3位。

    • 将行号在栏位的最左边进行显示

      test_mkdir]# nl -b a -n ln /etc/issue
      1       \S
      2       Kernel \r on an \m
      3     
      test_mkdir]# 
      

    从上述输出可见,nl命令在行号显示上比cat -n更丰富,可以限定行号在栏位种的显示位置以及栏位的位数。

  • more:一页一页显示文件的内容,但是当翻页到文件末尾的是否无法逆向翻页

    该命令的使用形式为:more file,在运行了该命令之后支持的按键操作如下:

    • 空格键Space : 代表向后翻一页
    • 回车键Enter :代表向下翻一行
    • :f :用于显示文件名以及当前的行数
    • q :表示立刻离开more命令,不再查看该文件的内容
    • b或者Ctrl+b :表示往回翻页,但是该操作只对文件有用,对于管道没用
    • / :表示输入要搜索的字符或者字符串,如果要重复查找该字符串,可以按下n键,但是很鸡肋,因为对于查找的字符或者字符串并不能高亮显示
  • less:与more命令类似,但是更好用,当翻页到文件末尾的时候,可以逆向往前翻页

    这个命令的使用形式为:less file,在运行了该命令之后支持的按键操作如下:

    • 空格键Space :向下翻一页
    • PageDown :向下翻一页
    • PageUp :向上翻一页
    • /字符或字符串 :向下查找字符或字符串
    • ?字符或字符串 :向上查找字符或字符串
    • n :重复前一个查找操作(与/或者?有关)
    • N :反向重复前一个查找(与/或者?有关)
    • g :定位到当前文件的第一行
    • G :定位到当前文件的最后一行(注意大小写)
    • q :离开less程序

    less还有很多功能,上述这些是最常用的操作,具体可以参见man less

  • head:只看文件指定的前几行

    该命令的使用形式为:head [-n number] file,选项参数的解释如下所示:

    • -n number :表示显示前number行

    该命令的使用示例如下所示:

    • 打印前10行

      test_mkdir]# head -n 10 /etc/man_db.conf
      # 
      #
      # This file is used by the man-db package to configure the man and cat paths.
      # It is also used to provide a manpath for those without one by examining
      # their PATH environment variable. For details see the manpath(5) man page.
      #
      # Lines beginning with `#' are comments and are ignored. Any combination of
      # tabs or spaces may be used as `whitespace' separators.
      #
      # There are three mappings allowed in this file:
      test_mkdir]# 
      
    • 不打印后面的120行,仅打印从倒数第121行开始往前的内容

      test_mkdir]# head -n -120 /etc/man_db.conf 
      # 
      #
      # This file is used by the man-db package to configure the man and cat paths.
      # It is also used to provide a manpath for those without one by examining
      # their PATH environment variable. For details see the manpath(5) man page.
      #
      # Lines beginning with `#' are comments and are ignored. Any combination of
      # tabs or spaces may be used as `whitespace' separators.
      #
      # There are three mappings allowed in this file:
      # --------------------------------------------------------
      test_mkdir]#
      

      上述的输出与前10行的而输出内容类似,只是多了一行,因为/etc/man_db.conf这个文件一共131行,所以上一条命令相当于打印前11行,等效于head -n 11 /etc/man_db.conf。具体如下所示:

      test_mkdir]# cat /etc/man_db.conf | wc -l
      131
      test_mkdir]# head -n 11 /etc/man_db.conf 
      # 
      #
      # This file is used by the man-db package to configure the man and cat paths.
      # It is also used to provide a manpath for those without one by examining
      # their PATH environment variable. For details see the manpath(5) man page.
      #
      # Lines beginning with `#' are comments and are ignored. Any combination of
      # tabs or spaces may be used as `whitespace' separators.
      #
      # There are three mappings allowed in this file:
      # --------------------------------------------------------
      test_mkdir]# 
      

    关于该命令的更多用法,参见man head

  • tail:只看文件末尾指定的后几行

    该命令的使用形式为:tail [-n number] file,该命令的选项参数解释如下:

    • -n number :代表显示后几行的内容
    • -f :表示持续刷新显示后面所接收到文件中的内容,直到按下Ctrl+c之后结束

    该命令的使用示例如下所示:

    • 显示后10行的内容:

      test_mkdir]# tail -n 10 /etc/man_db.conf 
      # formatted for a terminal of the given width, regardless of the width of
      # the terminal actually being used. This should generally be within the
      # range set by MINCATWIDTH and MAXCATWIDTH.
      #
      #CATWIDTH 0
      #
      #---------------------------------------------------------
      # Flags.
      # NOCACHE keeps man from creating cat pages.
      #NOCACHE
      test_mkdir]# 
      
    • 显示120行之后的内容

      test_mkdir]# tail -n +120 /etc/man_db.conf
      #
      # If CATWIDTH is set to a non-zero number, cat pages will always be
      # formatted for a terminal of the given width, regardless of the width of
      # the terminal actually being used. This should generally be within the
      # range set by MINCATWIDTH and MAXCATWIDTH.
      #
      #CATWIDTH 0
      #
      #---------------------------------------------------------
      # Flags.
      # NOCACHE keeps man from creating cat pages.
      #NOCACHE
      test_mkdir]# 
      

      上述输出是包含第120行的,具体如下所示:

      test_mkdir]# nl -b a -n rz /etc/man_db.conf
      ...
      000119  #MAXCATWIDTH    80
      000120  #
      000121  # If CATWIDTH is set to a non-zero number, cat pages will always be
      000122  # formatted for a terminal of the given width, regardless of the width of
      000123  # the terminal actually being used. This should generally be within the
      000124  # range set by MINCATWIDTH and MAXCATWIDTH.
      000125  #
      000126  #CATWIDTH       0
      000127  #
      000128  #---------------------------------------------------------
      000129  # Flags.
      000130  # NOCACHE keeps man from creating cat pages.
      000131  #NOCACHE
      test_mkdir]# 
      

      对比上述两个输出,可以发现,tail -n +120 /etc/man_db.conf的输出中是包含第120行的,表示从120行往后不管有多少内容都输出。

    • 持续监测文件末尾的内容,即新追加到该文件中的内容

      test_mkdir]# tail -f /etc/man_db.conf
      # formatted for a terminal of the given width, regardless of the width of
      # the terminal actually being used. This should generally be within the
      # range set by MINCATWIDTH and MAXCATWIDTH.
      #
      #CATWIDTH 0
      #
      #---------------------------------------------------------
      # Flags.
      # NOCACHE keeps man from creating cat pages.
      #NOCACHE
      
      

      上述会一直监测该文件的末尾,当有新内容追加到该文件的末尾的时候,就自动将其显示出来。直到按下Ctrl+c组合键之后才会退出tail命令。

    • 取出11-20行的内容

      test_mkdir]# head -n 20 /etc/man_db.conf | tail -n 10
      # --------------------------------------------------------
      # MANDATORY_MANPATH                     manpath_element
      # MANPATH_MAP           path_element    manpath_element
      # MANDB_MAP             global_manpath  [relative_catpath]
      #---------------------------------------------------------
      # every automatically generated MANPATH includes these fields
      #
      #MANDATORY_MANPATH                      /usr/src/pvm3/man
      #
      MANDATORY_MANPATH                       /usr/man
      test_mkdir]# 
      

      上述操作的思路就是先用head -n 20取出文件的前20行内容,然后将结果通过管道(|)传递给tail -n 10命令表示取出这20行中的后10行内容。下面使用nl命令验证上述输出是否符合要求:

      test_mkdir]# nl -b a -n rz /etc/man_db.conf
      ...
      000011  # --------------------------------------------------------
      000012  # MANDATORY_MANPATH                     manpath_element
      000013  # MANPATH_MAP           path_element    manpath_element
      000014  # MANDB_MAP             global_manpath  [relative_catpath]
      000015  #---------------------------------------------------------
      000016  # every automatically generated MANPATH includes these fields
      000017  #
      000018  #MANDATORY_MANPATH                      /usr/src/pvm3/man
      000019  #
      000020  MANDATORY_MANPATH                       /usr/man
      ...
      test_mkdir]# 
      

      可以看出,是符合要求到的。上述验证过程也可以通过cat -n /etc/man_db.conf | head -n 20 | tail -n 10来验证。

    更多关于该命令的使用方式,参见man tail

  • od:以二进制的形式读取文件的内容

    该命令的使用形式为:od [-t TYPE] file,选项参数解释如下:

    • -t :后面可以接各种类型(TYPE)的输出,比如:
      • a :利用默认的字符输出
      • c :使用ACSII字符输出
      • d [size] :利用十进制(decimal)输出数据,每个整数占用size个字节
      • f [size] :利用浮点数值(floating)输出数据,每个数占用size个字节
      • o [size] :利用八进制(octal)输出数据,每个整数占用size个字节
      • x [size] :利用十六进制(hexadecimal)输出数据,每个整数占用size个字节

    该命令的使用示例如下:

    • 将/usr/bin/passwd命令以ASCII字符输出

      test_mkdir]# od -t c /usr/bin/passwd | head -n 15
      0000000 177   E   L   F 002 001 001  \0  \0  \0  \0  \0  \0  \0  \0  \0
      0000020 003  \0   >  \0 001  \0  \0  \0   0   5  \0  \0  \0  \0  \0  \0
      0000040   @  \0  \0  \0  \0  \0  \0  \0 200   {  \0  \0  \0  \0  \0  \0
      0000060  \0  \0  \0  \0   @  \0   8  \0  \n  \0   @  \0 037  \0 036  \0
      0000100 006  \0  \0  \0 004  \0  \0  \0   @  \0  \0  \0  \0  \0  \0  \0
      0000120   @  \0  \0  \0  \0  \0  \0  \0   @  \0  \0  \0  \0  \0  \0  \0
      0000140   0 002  \0  \0  \0  \0  \0  \0   0 002  \0  \0  \0  \0  \0  \0
      0000160  \b  \0  \0  \0  \0  \0  \0  \0 003  \0  \0  \0 004  \0  \0  \0
      0000200   p 002  \0  \0  \0  \0  \0  \0   p 002  \0  \0  \0  \0  \0  \0
      0000220   p 002  \0  \0  \0  \0  \0  \0 034  \0  \0  \0  \0  \0  \0  \0
      0000240 034  \0  \0  \0  \0  \0  \0  \0 001  \0  \0  \0  \0  \0  \0  \0
      0000260 001  \0  \0  \0 005  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
      0000300  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
      0000320 350   \  \0  \0  \0  \0  \0  \0 350   \  \0  \0  \0  \0  \0  \0
      0000340  \0  \0      \0  \0  \0  \0  \0 001  \0  \0  \0 006  \0  \0  \0
      test_mkdir]#
      

      上述输出的最左边表示八进制数表示的字节数,第三行的0000020表示第16(2x8)个字节的内容。

    • 将/etc/issue这个文件以八进制的形式列出存储值与ASCII字符的对照表

      test_mkdir]# od -t oCc /etc/issue
      0000000 134 123 012 113 145 162 156 145 154 040 134 162 040 157 156 040
                \   S  \n   K   e   r   n   e   l       \   r       o   n    
      0000020 141 156 040 134 155 012 012
                a   n       \   m  \n  \n
      0000027
      test_mkdir]#
      

      上述输出的每个字符都可以对应到一个八进制数值,比如S对应的八进制值是123,换算成10进制数是(1x8^2 + 2x8^1 + 3x8^0 = 1x64 + 2x8 + 3 = 83)

    利用这个文件就可以将数据文件或者二进制文件的内容读取出来,虽然读取出来的内容默认是以十六进制的形式进行显示,但是可以通过-t c选项参数将数据内的字符以ASCII类型的字符显示出来,这个命令可以将二进制文件的内容做一个大致的输出,可以看出二进制文件的基本内容。

    如果要快速将一个字符串找出其对应的ASCII字符,可以执行如下命令:

    test_mkdir]# echo password | od -t oCc
    0000000 160 141 163 163 167 157 162 144 012
              p   a   s   s   w   o   r   d  \n
    0000011
    test_mkdir]#
    

    上述输出的字母与对应的数字中,数字部分为八进制数值。

    更的关于od命令的使用帮助,参见man od

  • touch :该命令可以用于创建空文件,也可以用与修改文件的时间戳

    该命令的使用形式为:touch [-acdmt] file,选项参数的解释如下:

    • -a :仅自定义访问时间,即atime
    • -c :仅修改文件的时间,如果文件不存在,则不创建该文件
    • -d :可以接自定义日期而不是使用当前日期作为选项值,也可以使用--date="日期或者时间"
    • -m :仅修改文件内容更改的时间,即mtime
    • -t :后买你可以接自定义的时间而不使用当前的时间,格式为"YYYYMMDDhhmm"

    该命令的使用示例如下所示:

    • 创建一个空文件,并检查文件的时间

      test_mkdir]# touch test_touch
      test_mkdir]# ls -l test_touch
      -rw-r--r-- 1 root root 0 Mar 21 21:22 test_touch
      

      检查上述文件的三个时间,具体如下所示:

      test_mkdir]# ls -l test_touch; ls -l --time=atime test_touch; ls -l --time=ctime test_touch
      -rw-r--r-- 1 root root 0 Mar 21 21:22 test_touch
      -rw-r--r-- 1 root root 0 Mar 21 21:22 test_touch
      -rw-r--r-- 1 root root 0 Mar 21 21:22 test_touch
      

      从上述输出可见,文件的内容修改时间、访问时间、属性修改时间这三个时间均被设置为了文件创建时的系统时间。

    • 复制~/.bashrc文件当当前目录,并检查日期

      test_mkdir]# cp -a ~/.bashrc bashrc
      test_mkdir]# date; ls -l bashrc; ls -l --time=atime bashrc; ls -l --time=ctime bashrc
      Sun Mar 21 21:28:03 CST 2021
      -rw-r--r--. 1 root root 176 May 11  2019 bashrc
      -rw-r--r--. 1 root root 176 Mar 20 20:31 bashrc
      -rw-r--r--. 1 root root 176 Mar 21 21:27 bashrc
      

      上述输出中,分别列出了文件内容的修改时间、文件的访问时间、文件的属性修改时间。由于上述文件复制过来的时候复制了属性信息,所以第三个属性更改时间设置为了当前时间。

    • 修改上述的bashrc的日期设置为2天之前,并检查日期

      test_mkdir]# touch -d '2 days ago' bashrc
      test_mkdir]# date; ls -l bashrc; ls -l --time=atime bashrc; ls -l --time=ctime bashrc
      Sun Mar 21 21:32:37 CST 2021
      -rw-r--r--. 1 root root 176 Mar 19 21:32 bashrc
      -rw-r--r--. 1 root root 176 Mar 19 21:32 bashrc
      -rw-r--r--. 1 root root 176 Mar 21 21:32 bashrc
      

      从上述输出中可以看出,文件的内容修改时间、文件的访问时间都被设置为了2天之前,而属性修改时间仍然记录的是当前时间。

    • 将bashrc文件的时间设置为202001010520

      test_mkdir]# touch -t 202001010520 bashrc
      test_mkdir]# ls -l bashrc; ls -l --time=atime bashrc; ls -l --time=ctime bashrc
      -rw-r--r--. 1 root root 176 Jan  1  2020 bashrc
      -rw-r--r--. 1 root root 176 Jan  1  2020 bashrc
      -rw-r--r--. 1 root root 176 Mar 21 21:39 bashrc
      

      上述输出可见,文件的内容修改时间mtime、文件的访问时间atime都发生了更改,但是文件的属性修改时间ctime记录的仍然时当前时间。

    • 改变bashrc文件的权限,然后查看时间戳

      test_mkdir]# chmod u+x bashrc
      test_mkdir]# date; ls -l bashrc; ls -l --time=atime bashrc; ls -l --time=ctime bashrc
      Sun Mar 21 21:41:18 CST 2021
      -rwxr--r--. 1 root root 176 Jan  1  2020 bashrc
      -rwxr--r--. 1 root root 176 Jan  1  2020 bashrc
      -rwxr--r--. 1 root root 176 Mar 21 21:40 bashrc
      

      属性修改时间记录的仍然时当前的时间。

    通过touch命令可以创建空文件并且可以秀嘎文件的日期和时间,需要注意的是,即便我们复制文件的时候复制属性信息,也无法修改文件的ctime这个属性更改时间,ctime记录了这个文件最近的状态被修改的时间,在实际使用中,通常更关注的是mtime,即文件内容的修改时间。

4.3 文件与目录的权限管理命令

前面的文件元数据信息中,包含了文件的权限、所有者、所属组等信息,要修改文件的权限、所有关系,这部分将会介绍文件的基本权限与所属关系、默认权限等几块内容。分别介绍如下:

4.3.1 文件的基本权限与所属关系

文件的基本权限此前介绍文件的元数据信息部分简单介绍了,即三个一组的rwx组成,如果对应的位位-,则表示失去该位权限。这几个字符的对应含义如下:

  • r :读取权限,八进制数值为4
  • w :写入权限,八进制数值为2
  • x :执行权限,八进制数值为1
  • - :表示改为权限失去,八进制数值为0

所以rwx和-的几种组合方式中,对应的数字值分别为:

  • rwx :7(即4 + 2 + 1)
  • rw- : 6(即4 + 2 + 0)
  • r-x :5(即4 + 0 + 1)
  • r-- :4(即4 + 0 + 0)
  • -wx :3(即0 + 2 + 1)
  • -w- :2(即0 + 2 + 0)
  • --x :1(即0 + 0 + 1)
  • --- :0(即0 + 0 + 0)

由于权限位对应三组,分别为:

  • user :所有者,用u表示
  • group :所属组,用g表示
  • other :其他用户和组,用o表示
  • all :所有上述三组,用a表示

涉及到文件和目录的权限和所属关系的命令如下:

  • chmod :修改文件的权限

    该命令最常的使用形式主要有两种:

    • chmod [options] 777 file/directory

      这种命令形式是将三组用户角色的rwx权限转换为八进制数字给出,三个数字代表三个用户角色对该文件所拥有的权限。下面以test_touch这个文件为例进行说明,具体如下所示:

      test_mkdir]# ls -l test_touch
      -rw-r--r-- 1 root root 0 Mar 21 21:22 test_touch
      

      上述输出显示,该文件的权限换算为八进制表示位644,要修改该文件的权限为所有者具有完全的读写执行权限,所有者具有读和执行的权限,其他用户和组不具有任何读写执行权限,具体如下:

      test_mkdir]# chmod 750 test_touch
      test_mkdir]# ls -l test_touch
      -rwxr-x--- 1 root root 0 Mar 21 21:22 test_touch
      

      上述输出中显示,通过750这个组合权限值,将所有者的权限设置为了读写执行权限,将所属组设置为了读取和执行权限,同时拿掉了其他用户和组的所有权限。

      如果要修改目录的权限,要将./test/test1目录的权限设置为所有者具有完全的读写执行权限,而所属组和其他用户和组不具有任何权限,可以执行如下命令:

      test]# ls -ld test1
      drwxr-xr-x 3 root root 19 Mar 21 16:01 test1
      test]# chmod 700 test1
      test]# ls -ld test1
      drwx------ 3 root root 19 Mar 21 16:01 test1
      

      从上述输出可见,对目录的操作,直作用在了当前目录,而不会影响该目录下面的文件和子目录。如果要递归修改该目录下的文件和目录的权限,则可以在上述命令的基础上增加-R这个选项,表示递归修改该目录以及其子目录和文件的权限。具体如下所示:

      test]# ls -l test1/
      total 0
      drwxr-xr-x 3 root root 19 Mar 21 16:01 test3
      test]# chmod -R 750 test1/
      test]# ls -l test1/
      total 0
      drwxr-x--- 3 root root 19 Mar 21 16:01 test3
      test]# ls -ld test1/
      drwxr-x--- 3 root root 19 Mar 21 16:01 test1/
      

      从上述输出可见,当前目录和子目录的权限都被修改了。

    • chmod [options] ugo=rwx file/direcotry

      这种修改形式更加简洁直观,无需进行数值的计算。

      在此仅列举一种使用方式,比如修改./test/passwd的权限为rwxr-xr-x的形式,可以执行如下命令:

      test]# ls -l passwd 
      -rw-r--r-- 1 root root 3572 Mar 21 16:40 passwd
      test]# chmod u=rwx,g=rx,o=rx passwd
      test]# ls -l passwd
      -rwxr-xr-x 1 root root 3572 Mar 21 16:40 passwd
      

      如果要从全部三个用户角色中减去执行权限,可以写称如下形式:

      test]# chmod a-x passwd
      test]# ls -l passwd
      -rw-r--r-- 1 root root 3572 Mar 21 16:40 passwd
      

      上述的a-x即表示减去所有用户角色的执行权限,将-号替换为+表示给三种用户角色增加执行权限。不再列出

    该命令的更多用法,参见man chmod

  • chown :修改文件或目录的所有关系,比如所有者、所属组等信息

    该命令的使用形式为:chown [options] owner:group file,该命令中,将user:group中的冒号替换为英文句点符号也是可以的,此时的命令形式为chown [options] owner.group file

    该命令的使用示例如下:

    将./test/passwd文件的所有者和所属组修改为nobody和qemu,具体如下所示:

    test]# ls -l passwd
    -rw-r--r-- 1 root root 3572 Mar 21 16:40 passwd
    test]# chown nobody:qemu passwd
    test]# ls -l passwd
    -rw-r--r-- 1 nobody qemu 3572 Mar 21 16:40 passwd
    

    此外,也可以使用英文句点符号替换英文冒号,将所有者和所属组修改为root,执行如下命令:

    test]# chown root.root passwd
    test]# ls -l passwd
    -rw-r--r-- 1 root root 3572 Mar 21 16:40 passwd
    

    上述两种形式的效果是相同的。

    另外,将上述的文件替换为目录,效果也是一样的,但是只对当前目录有效,而不会递归应用到该目录之下的子目录和文件。如果要递归应用到当前目录以及其子目录和文件,则需要使用选项-R。比如将./test目录以及其下的子目录和文件的所有者修改为nobody,所属组修改为qemu,执行如下命令即可:

    test]# ls -l test1
    total 4
    -rw-r--r-- 1 root root 3572 Mar 21 22:41 passwd
    drwxr-x--- 3 root root   19 Mar 21 16:01 test3
    test]# ls -ld test1
    drwxr-x--- 3 root root 33 Mar 21 22:41 test1
    test]# chown -R nobody.qemu test1
    test]# ls -ld test1
    drwxr-x--- 3 nobody qemu 33 Mar 21 22:41 test1
    test]# ls -l test1
    total 4
    -rw-r--r-- 1 nobody qemu 3572 Mar 21 22:41 passwd
    drwxr-x--- 3 nobody qemu   19 Mar 21 16:01 test3
    

    更多该命令的使用信息,参见man chown

  • chgrp :修改文件或目录的所属组信息

    该命令的使用形式与上述的chown命令的形式类似,就是省略掉了用户和分隔符,只保留用户组部分。具体为:chgrp [options] group file/directory

    将./test/passwd文件的所属组修改为qemu,执行如下命令即可:

    test]# ls -l passwd 
    -rw-r--r-- 1 root root 3572 Mar 21 16:40 passwd
    test]# chgrp qemu passwd
    test]# ls -l passwd
    -rw-r--r-- 1 root qemu 3572 Mar 21 16:40 passwd
    

    将./test目录以及其下的子目录和文件的所属组修改为root,执行如下命令:

    test]# ls -l test1/
    total 4
    -rw-r--r-- 1 nobody qemu 3572 Mar 21 22:41 passwd
    drwxr-x--- 3 nobody qemu   19 Mar 21 16:01 test3
    test]# ls -ld test1/
    drwxr-x--- 3 nobody qemu 33 Mar 21 22:41 test1/
    test]# chgrp -R root test1/
    test]# ls -ld test1/
    drwxr-x--- 3 nobody root 33 Mar 21 22:41 test1/
    test]# ls -l test1/
    total 4
    -rw-r--r-- 1 nobody root 3572 Mar 21 22:41 passwd
    drwxr-x--- 3 nobody root   19 Mar 21 16:01 test3
    

    上述即完成了修改操作,选项-R表示递归应用修改。

    关于该命令的更多使用信息,参见man chgrp

上述即为基本的文件和目录权限和所有关系管理。

4.3.2 默认权限与权限掩码

用户创建文件和目录的时候,默认的权限是不一样的,

  • 创建文件时,默认权限是666,即-rw-rw-rw-
  • 创建目录时,默认权限是777,即drwxrwxrwx

而文件或目录创建时最终能具有的权限,除了取决于默认权限之外,还取决于权限掩码:umask,即用默认权限值减去权限掩码值,为最终获得的权限。权限掩码值有两种显示方式:

  • umask :直接以数字的形式显示权限掩码值,默认是0022,具体如下:

    test]# umask
    0022
    
  • umask -S :符号形式显示,具体如下所示:

    test]# umask -S
    u=rwx,g=rx,o=rx
    

上述两种输出形式的理解方式是不一样的,第一种输出形式,表示从默认权限中移除所属组和其他用户角色的写入权限;第二种输出形式,表示应用权限掩码之后可以保留的权限,此处不具有的权限,表示要从默认权限中减去的权限。虽然两者的输出形式上的差异造成的理解方式不同,但是最终效果是一样的。

所以当创建文件或目录的时候,两者的权限分别为:

  • 创建文件时的默认权限为666(-rw-rw-rw-),权限掩码为0022(-----w–w-),即从默认权限中拿掉写入权限,所以最终获的权限为644(-rw-r–r--)。

    注意,此处并不是直接用666-0022这么简单的计算,掩码操作的背后运算逻辑应该是数字的按位异或运算,在python中演示如下:

    In [108]: 7 ^ 2
    Out[108]: 5
    
    In [109]: bin(5)
    Out[109]: '0b101'
    
    In [110]: bin(7)
    Out[110]: '0b111'
    
    In [111]: bin(2)
    Out[111]: '0b10'
    

    上述输出中,7与2计算按位异或值,结果为5,将7、2和5这三个数字分别转换为2进制数字,得到的结果分别为:

    • 111 :数字7的二进制值,表示为权限位的方式为rwx
    • 101 :数字5的二进制值,表示为权限位的方式为r-x
    • 010 :数字2的二进制值,表示为权限位的方式位-w-

    从上述结果可以看出,默认权限7与权限掩码2的异或结果为5,即从rwx中拿掉w这个写入权限的结果。

    文件的默认权限6与权限掩码2的按位异或计算方式相同,此处不再展开。

    使用touch命令创建一个空文件,查看该文件的最终权限,具体如下所示:

    test]# touch permission
    test]# ls -l permission
    -rw-r--r-- 1 root root 0 Mar 21 23:15 permission
    

    可以看出,文件的最终权限为644,即-rw-r–r--。

  • 创建目录时的默认权限为777(drwxrwxrwx),权限掩码为0022(-----w--w-),即从默认权限中拿掉写入权限,所以最终获得的权限为755(drwxr-xr-x)。

    使用mkdir命令创建一个空目录,查看其权限,如下所示:

    test]# mkdir permission_dir
    test]# ls -ld permission_dir
    drwxr-xr-x 2 root root 6 Mar 21 23:17 permission_dir
    

    从上述输出中可以看出,目录最终获得权限为755,即drwxr-xr-x。

umask的值是可以更改的,修改为不同的值,表示从默认权限中拿掉对应的权限。比如将umask的值修改为0003,表示拿掉其他用户和组的写入和执行权限,保持所属组的权限为默认权限不变。具体如下所示:

test]# umask 0003
test]# umask
0003
test]# touch new_umask_f
test]# mkdir new_umask_d
test]# ls -ld new_umask_d
drwxrwxr-- 2 root root 6 Mar 21 23:22 new_umask_d
test]# ls -l new_umask_f 
-rw-rw-r-- 1 root root 0 Mar 21 23:22 new_umask_f

从上述输出中可以看出,由于文件的默认权限中本身就没有执行权限,所以只是拿掉了写入权限;而目录的默认权限中则包含了执行权限,所以拿掉了写入和执行权限。从这里也可以看出,最终能获得权限,除了取决于权限掩码值之外,更取决于默认权限中具有什么权限。

4.4 命令与文件查找相关的命令

文件和命令的查找命令中,最常用的就是就是which命令,此外还有两个不太常用的命令,分别为whereislocate

  • which命令:用于查找shell命令的完整路径

    该命令的使用形式为:which [options] -- command,常用的选项为-a,如果不加-a选项,则which命令会从PATH环境变量中记录的路径里面搜索到第一个匹配的结果即结束搜索;如果加上-a选项,则会列出所有匹配的结果。具体如下所示:

    test]# which which
    alias which='(alias; declare -f) | /usr/bin/which --tty-only --read-alias --read-functions --show-tilde --show-dot'
            /usr/bin/which
    test]# which -a which
    alias which='(alias; declare -f) | /usr/bin/which --tty-only --read-alias --read-functions --show-tilde --show-dot'
            /usr/bin/which
    /usr/bin/which
    test]#
    

    上述输出的i一种形式不带选项-a,所以只匹配到了别名;而第二个带有选项-a多匹配出来一个/usr/bin/which命令。

    注意,由于which命令是通过搜索PATH环境变量查找命令,所以一些bash内建命令是无法通过which命令查找到的。具体如下所示:

    test]# which history
    /usr/bin/which: no history in (/usr/local/python3.8.6/bin:/usr/local/git/bin:/usr/local/python3.8.6/bin:/usr/local/git/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin)
    

    由于history是内建命令,所以无法被搜索到。但是其他一些内建命令则是可以搜索到的,具体可以通过man history命令进入到内建命令的界面,或者通过man bash查找内建命令,然后进行which的测试,一些命令是可以被找到的。

  • whereis :从一些特定的目录中查找文件

    该命令的使用形式为:whereis [-bmsu] file/directory,选项参数的解释如下:

    • -l :列出whereis会去查找的目录
    • -b :之查找二进制格式的文件
    • -m :之查找在manual路径下的文件
    • -s :之查找源文件
    • -u :查找不在上述三个项目中的其他特殊文件

    使用示例如下:

    test]# whereis ifconfig
    ifconfig: /usr/sbin/ifconfig /usr/share/man/man8/ifconfig.8.gz
    test]# whereis ip
    ip: /usr/sbin/ip /usr/share/man/man7/ip.7.gz /usr/share/man/man8/ip.8.gz
    

    其他选项应用:

    test]# whereis -b ip
    ip: /usr/sbin/ip
    test]# whereis -m ip
    ip: /usr/share/man/man7/ip.7.gz /usr/share/man/man8/ip.8.gz
    test]# 
    

    由于whereis命令只是查找几个固定的路径,并不是全系统查找,所以查找速度很快;但是由于查找的路径有限,所以能查找的文件也有限,实际使用并不多。

    要查看whereis命令搜索那些路径,可以执行如下命令:

    [root@amdhost test]# whereis -l | head -n 20
    bin: /usr/bin
    bin: /usr/sbin
    bin: /usr/lib
    bin: /usr/lib64
    bin: /etc
    bin: /usr/etc
    bin: /usr/games
    bin: /usr/local/bin
    bin: /usr/local/sbin
    bin: /usr/local/etc
    bin: /usr/local/lib
    bin: /usr/local/games
    bin: /usr/include
    bin: /usr/local
    bin: /usr/libexec
    bin: /usr/share
    bin: /usr/local/python3.8.6/bin
    bin: /usr/local/git/bin
    man: /usr/share/man/man7
    man: /usr/share/man/man0p
    
  • locate :从自己的/var/lib/mlocate数据库中搜索文件

    该命令的使用形式为:locate [-ir] keyword,选项参数的解释如下:

    • -i :忽略大小写的差异
    • -c :不输出文件名,仅计算查找到的文件数量
    • -l :接数字参数,表示输出几行,比如-l 5表示输出5行
    • -S :输出locate命令所使用的数据库文件的相关信息,包括数据库记录的文件和目录的数量等
    • -r :其后接正则表达式

    由于locate命令实际查找的并不是文件系统的全部路径,而是去搜索一个数据库文件——/var/lib/mlocate/mlocate.db,如果在执行当次搜索的时候,并未更新该数据库,那么如果搜索的结果恰好没有被记录到这个数据库中,那么即便系统上存在,也无法搜索到;此时就需要执行命令updatedb更新该数据库,然后再次执行locate命令搜索该数据库即可。updatedb命令的配置文件为/etc/updatedbconf,关于该配置文件的详细帮助信息,可以执行命令man 5 updatedb.conf进行查看。

    该命令的使用示例如下:

    test]# locate -l 5 passwd
    locate: can not stat () `/var/lib/mlocate/mlocate.db': No such file or directory
    test]# updatedb
    test]# locate -l 5 passwd
    /data/backup/root_home/.vnc/passwd
    /data/backup/root_home/softwares/Python-3.6.8/Lib/test/keycert.passwd.pem
    /data/backup/root_home/softwares/Python-3.6.8/Lib/test/ssl_key.passwd.pem
    /data/backup/root_home/softwares/git-2.24.0/t/lib-httpd/passwd
    /data/repositories/base/Packages/passwd-0.79-5.el7.x86_64.rpm
    test]#
    

    上述由于是初次使用这个命令,并未初始化过该数据库,所以此时会提示无法找到mlocate.db这个文件。此时就需要执行updatedb命令更新数据库,然后再执行locate命令即可。

5. 交互式与非交互式文本内容查找

所谓的交互式文本内容查找,是通过vim编辑器对文件进行编辑;非交互式文本内容查找则是通过sed流式编辑器来完成。

比如要删除一个文件中的空白行,以/etc/profile这个文件为例,将这个文件复制到/tmp目录中,分别使用这两个编辑器实现如下:

  • 通过vim编辑器实现:

    ./profile文件的原始内容如下所示:

    # /etc/profile
    
    # System wide environment and startup programs, for login setup
    # Functions and aliases go in /etc/bashrc
    
    # It's NOT a good idea to change this file unless you know what you
    # are doing. It's much better to create a custom.sh shell script in
    # /etc/profile.d/ to make custom changes to your environment, as this
    # will prevent the need for merging in future updates.
    
    pathmunge () {
        case ":${PATH}:" in
            *:"$1":*)
                ;;  
            *)  
                if [ "$2" = "after" ] ; then
                    PATH=$PATH:$1
                else
                    PATH=$1:$PATH
                fi  
        esac
    }
    
    
    if [ -x /usr/bin/id ]; then
        if [ -z "$EUID" ]; then
            # ksh workaround
            EUID=`/usr/bin/id -u` 
            UID=`/usr/bin/id -ru`
        fi  
        USER="`/usr/bin/id -un`"
    

    上述为部分内容,在vim中删除空白行的效果如下所示:

    # /etc/profile
    # System wide environment and startup programs, for login setup
    # Functions and aliases go in /etc/bashrc
    # It's NOT a good idea to change this file unless you know what you
    # are doing. It's much better to create a custom.sh shell script in
    # /etc/profile.d/ to make custom changes to your environment, as this
    # will prevent the need for merging in future updates.
    pathmunge () {
        case ":${PATH}:" in
            *:"$1":*)
                ;;
            *)
                if [ "$2" = "after" ] ; then
                    PATH=$PATH:$1
                else
                    PATH=$1:$PATH
                fi
        esac
    }
    if [ -x /usr/bin/id ]; then
        if [ -z "$EUID" ]; then
            # ksh workaround
            EUID=`/usr/bin/id -u`
            UID=`/usr/bin/id -ru`
        fi
        USER="`/usr/bin/id -un`"
        LOGNAME=$USER
        MAIL="/var/spool/mail/$USER"
    fi
    # Path manipulation
    if [ "$EUID" = "0" ]; then
    :g/\v^$/d 
    

    上述为vim中删除空白行之后的结果,其中第32行为vim中执行的命令:g/\v^$/d,解释如下:

    • : :表示进入末行模式
    • g :表示所有行,全部文件
    • \v :表示启用拓展正则表达
    • ^$ :表示以换行符开头的行,即空白行,此处无法匹配包含空格符或者制表符的空白行,如果要匹配空白行或者包含制表符的行,可以写成如下形式:
      • **[ |\t]{1,}**表示匹配一个或者多个空白符或者制表符
    • d :表示删除这个命令

    另外,要在vim中,将制表符设置为4个空格,可以在vim的编辑模式下按下:set ts=4即可完成。

  • 通过sed流式编辑器实现

    通过sed实现的命令如下所示:

    test_mkdir]# sed -e '/^$/d' profile | head -n 30
    # /etc/profile
    # System wide environment and startup programs, for login setup
    # Functions and aliases go in /etc/bashrc
    # It's NOT a good idea to change this file unless you know what you
    # are doing. It's much better to create a custom.sh shell script in
    # /etc/profile.d/ to make custom changes to your environment, as this
    # will prevent the need for merging in future updates.
    pathmunge () {
        case ":${PATH}:" in
            *:"$1":*)
                ;;
            *)
                if [ "$2" = "after" ] ; then
                    PATH=$PATH:$1
                else
                    PATH=$1:$PATH
                fi
        esac
    }
    if [ -x /usr/bin/id ]; then
        if [ -z "$EUID" ]; then
            # ksh workaround
            EUID=`/usr/bin/id -u`
            UID=`/usr/bin/id -ru`
        fi
        USER="`/usr/bin/id -un`"
        LOGNAME=$USER
        MAIL="/var/spool/mail/$USER"
    fi
    # Path manipulation
    

    上述命令并没有真正将更改写入到原始文件中,而仅仅是打印到了标准输出中。要将修改应用到原始文件中,可以增加-i选项,此时的命令为:sed -i -e '/^$/d' profile

    注意:慎用-i选项,因为该选项会将所有更改操作直接作用到原始文件上。

6. Reference

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ThinkPHP 是一个优秀的PHP框架,可以帮助我们快速开发高质量的 Web 应用程序。因此,使用 ThinkPHP 来开发 OA 系统是非常方便和实用的。下面我们来讨论一下如何使用 ThinkPHP 来开发一个人力资源管理系统。 1. 创建数据库 首先,我们需要创建一个数据库来存储我们的数据。在该数据库中,我们需要创建一些表来存储员工信息、部门信息、职位信息、薪资信息等等。我们可以使用 MySQL 数据库来创建这些表。 2. 创建 ThinkPHP 项目 在创建完数据库之后,我们需要创建一个 ThinkPHP 项目来实现我们的 OA 系统。我们可以使用 ThinkPHP 官方提供的命令行工具来创建项目,具体命令如下: ``` composer create-project topthink/think oa ``` 执行完上述命令后,会在当前目录下创建一个名为 oa 的 ThinkPHP 项目。 3. 配置数据库 在项目创建好之后,我们需要配置数据库连接信息。在项目的 `.env` 文件中,我们可以配置数据库的相关信息,如下所示: ``` # 数据库类型 DB_CONNECTION=mysql # 数据库地址 DB_HOST=127.0.0.1 # 数据库端口 DB_PORT=3306 # 数据库名 DB_DATABASE=oa # 数据库用户名 DB_USERNAME=root # 数据库密码 DB_PASSWORD= ``` 我们需要根据自己的实际情况来修改这些配置项。 4. 创建控制器和模型 在 ThinkPHP 中,我们可以通过创建控制器和模型来实现 OA 系统的功能。我们可以使用命令行工具来快速创建控制器和模型,具体命令如下: ``` # 创建控制器 php think make:controller Index # 创建模型 php think make:model User ``` 执行完上述命令后,会在项目中生成一个名为 Index 的控制器和一个名为 User 的模型。 5. 实现功能 在创建好控制器和模型之后,我们就可以开始实现 OA 系统的功能了。比如,我们可以实现员工信息的添加、修改、删除、查询等操作,部门信息的添加、修改、删除、查询等操作,职位信息的添加、修改、删除、查询等操作,薪资信息的添加、修改、删除、查询等操作等等。我们可以在控制器中编写处理逻辑,在模型中编写数据库操作。最后,我们可以使用视图来展示数据。 6. 测试系统 在实现完功能之后,我们需要对系统进行测试。我们可以使用浏览器或者 Postman 等工具来测试系统的功能是否正常。如果有问题,我们可以根据错误提示来进行调试和修复。 总之,使用 ThinkPHP 来开发 OA 系统是非常方便和实用的。我们可以根据自己的实际情况来定制系统的功能和界面,从而更好地管理公司的人力资源。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值