首页 > 代码库 > 第一章 文件类基础命令

第一章 文件类基础命令


本文目录:

1.1 关于路径

1.2 查看目录内容

1.3 文件的时间戳(atime/ctime/mtime)

1.4 文件/目录的创建和删除

1.5 查看文件类型file命令

1.6 文件/目录复制和移动

1.7 查看文件内容

1.8 文件查找类命令


1.1 关于路径

Linux中分绝对路径和相对路径,绝对路径一定是从/开始写的,相对路径不从根开始写,还可能使用路径符号。

路径展开符号:

.           :(一个点)表示当前目录

..          :(两个点)表示上一层目录

-           :(一个短横线)表示上一次使用的目录,例如从/tmp直接切换到/etc下,-就表示/tmp

~          :(波浪符号)表示用户的家目录,例如"~account"表示account用户的家目录

/dir/和/dir:一般都表示dir目录和dir目录中的文件。但在有些地方会严格区分是否加尾随斜线,此时对于加了尾随斜线的表示此目录中的文件,不加尾随斜线的表示该目录本身和此目录中的文件

切换路径用cd命令;

显示当前所在目录用pwd命令。若当前所在目录为链接目录,使用pwd显示的将是链接自身,使用-P选项将定位到链接的原始目录。

[root@ansible6_node1 ~]# ll ; cd tmp; pwd; pwd -P
total 0
lrwxrwxrwx 1 root root 4 May 30 19:17 tmp -> /tmp
/root/tmp
/tmp

获取文件名使用basename命令,获取文件所在目录使用dirname命令。注意,这两个命令其实不太完善,它不会检查文件或目录是否存在,只要写出来了就会去获取。

[root@xuexi tmp]# basename /etc/shadow
shadow

[root@xuexi tmp]# basename /etc/
etc

[root@xuexi tmp]# dirname /etc/shadow
/etc

[root@xuexi tmp]# dirname /etc/ # 对目录使用dirname获取的是上级目录
/
 
[root@server1 ~]# dirname /kalsldk/kdkskks/djfjdjdjsj # 获取不存在的目录
/kalsldk/kdkskks

1.2 查看目录内容(ls和tree)

ls命令列出目录中的内容,和dir命令完全等价。tree命令按树状结构递归列出目录和子目录中的内容,而ls使用-R选项时才会递归列出。

注意:ls的结果中是以制表符分隔多个文件的。

1. ls命令

ls的各个选项说明如下:

-l:(long)长格式显示,即显示属性等信息(包括mtime)。注意:显示的目录大小是节点所占大小。像win一样计算目录大小时包括文件大小要用du -sh

-c:列出ctime

-u:列出atime

-d:(direcorty)查看目录本身属性信息,不查看目录里面的东西。不加-d会查看里面文件的信息

-a:会显示所有文件,包括两个相对路径的文件"."和".."以及以点开头的隐藏文件

-A:会列出绝大多数文件,即忽略两个相对路径的文件"."和".."

-h:(human)人类可读的格式,将字节换成k,将K换成M,将M换成G

-i:(inode)权限属性的前面加上一堆数字

-p:对目录加上/标识符以作区分

-F:对不同类型的文件加上不同标识符以作区分,对目录加的文件也是/

-t:按修改时间排序内容。不加任何改变顺序的选项时,ls默认按照字母顺序排序

-r:反转排序

-R:递归显示

--color:显示颜色

注意,ls以-h显示文件大小时,一般显示的都是不带B的单位,如K/M/G,它们的转换比例是1024,如果显示的但是是带了B的,如KB/MB/GB,则它们的转换比例为1000而非1024,一般很少显示带B的大小。

以下是使用ls -l显示文件长格式的属性。

[root@xuexi ~]# ll /tmp
drwxr-xr-x 2 root root 4096 Mar 26 16:44 test1

可以查出7列属性。

技术分享

2. tree命令

有可能tree命令不存在,需要安装tree包才有(安装:yum -y install tree)。

tree命令的选项说明如下:

【 匹配选项:】

-L:用于指定递归显示的深度,指定的深度必须是大于0的整数。

-P:用于显示统配符匹配模式的目录和文件,但是不管是否匹配,目录一定显示。

-I:用于显示除被通配符匹配外的所有目录和文件。

【 显示选项:】

-a:用于显示隐藏文件,默认不显示。

-d:指定只显示目录。

-f:指定显示全路径。

-i:不缩进显示。和-f一起使用很有用。

-p:用于显示权限位信息。

-h:用于显示大小。

-u:显示username或UID(当没有username时只能显示UID了)。

-g:显示groupname或GID。

-D:显示文件的最后一次Mtime。

--inodes:显示inode号。

--device:显示文件或目录所属的设备号。

-C:显示颜色。

【 输出选项:】

-o filename:指定将tree的结果输出到filename文件中。

下图是一个较全的输出结果。

技术分享

1.3 文件的时间戳(atime/ctime/mtime)

文件的时间属性有三种:atime/ctime/mtime。atime是access time,即上一次的访问时间;mtime是modify time,是文件的修改时间;ctime是change time,也是文件的修改时间。

但mtime只有修改文件内容才会改变,更准确的说是修改了它的data block部分;而ctime是修改文件属性时改变的,确切的说是修改了它的元数据部分,例如重命名文件,修改文件所有者,移动文件(移动文件没有改变datablock,只是改变了其inode指针,或文件名)等.当然,修改文件内容也一定会改变ctime(修改文件内容至少已经修改了inode记录上的mtime,这也是元数据),也就是说mtime的改变一定会引起ctime的改变。

对目录而言,考虑目录文件的data block,可知在目录中创建、删除文件以及目录内其他任意文件操作都会改变mtime,因为目录里的任何东西都是目录中的内容;而目录的ctime,除了目录的mtime引起ctime改变之外,对目录本身的元数据修改也会改变ctime。

总结下:

(1).atime只在文件被打开访问时才改变,若不是打开文件编辑内容(如重定向内容到文件中),则ctime和mtime的改变不会引起atime的改变;

(2).mtime的改变一定引起ctime的改变,而atime的改变不总是会影响ctime(如touch atime时会改变ctime,但cat文件时不会改变ctime),atime也是文件的元数据信息,它改变了为什么不会改变ctime?(待解答)

1.4 文件/目录的创建和删除

1.4.1 创建目录mkdir

mkdir [-mp] 目录名

-m:表示创建目录时直接设置权限

-p:表示递归创建多层目录,即上层目录不存在时也会直接将其创建出来(parent)

[root@xuexi ~]# mkdir /tmp/test1 # 在tmp目录中创建一个test1目录
[root@xuexi ~]# mkdir -m 711 /tmp/test2 # 直接创建test2时就赋予权限711
[root@xuexi ~]# mkdir -p /tmp/test3/test4/test5 # 创建test5,此时会将不存在的test3和test4目录也创建好

1.4.2 创建文件touch

touch file_name

[root@xuexi ~]# touch /tmp/test1/test1.txt
[root@xuexi ~]# touch {1..10} # 创建文件名为1-10的文件

多个{}还可以交换扩展。类似(a+b)(c+d)=ac+ad+bc+bd。

[root@xuexi ~]# touch {a,b}_{c,d} # 创建a_c、a_d、b_c、b_d四个文件

touch主要是修改文件的时间戳信息,当touch的文件不存在时就自动创建该文件。可以使用 touch –c 来取消创建动作。

touch可以更改最近一次访问时间(atime),最近一次修改时间(mtime),文件属性修改时间(ctime),这些时间可以通过命令stat file来查看。其中ctime是文件属性上的更改,即元数据的更改,比如修改权限。

touch -a修改atime,-m修改mtime,没有修改ctime的选项。因为使用touch改变atime或mtime,同时也都会改变ctime,虽说atime并不总是会影响ctime(如cat文件时)。

-t选项表示使用"[[CC]YY]MMDDhhmm[.ss]"格式的时间替代当前时间。

touch -a -t 201212211212 file    # 将file文件的atime修改为2012年12月21号12点12分

-d选项表示使用指定的字符串描述时间格式来替代当前时间,如"3 days ago","next Sunday"等很多种格式。

所以,touch命令选项说明如下:

-c:强制不创建文件

-a:修改文件access time(atime)

-m:修改文件modification time(mtime)

-t:使用"[[CC]YY]MMDDhhmm[.ss]"格式的时间替代当前时间

-d:使用字符串描述的时间格式替代当前时间

1.4.3 删除文件/目录

rm [-rfi] file_name

-r:表示递归删除,删除目录时需要加此参数

-i:询问是否删除(yes/no)

-f:强制删除,不进行询问

[root@xuexi ~]# rm -rf /tmp/test2

删除空目录时还可以使用rmdir。

在删除文件之前,一定一定要确定是否真的删除。最好使用rm -i(默认已经在~/.bashrc中定义了该别名),除非在脚本中,否则不要轻易使用-f选项。已经有非常多的人不小心rm -rf *和rm -rf /NNNNN了。例如想删除"rm –rf /abc*",结果习惯性的多敲了一个空格"rm –rf /abc *",完了。

1.5 查看文件类型file命令

这是一个简单查看文件类型的命令,查看文件是属于二进制文件还是数据文件还是ASCII文件。

[root@xuexi tmp]# file /etc/aliases.db
/etc/aliases.db: Berkeley DB (Hash, version 9, native byte-order) # 数据文件

[root@xuexi tmp]# file ~/.bashrc
/root/.bashrc: ASCII text # ASCII文件

[root@xuexi tmp]# file /bin/ls
/bin/ls: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, stripped

除了基本的查看文件类型的功能外,file还有一个"-s"选项,是一个超强力的选项,可以查看设备的文件系统类型。像有些分区工具如parted在分区时是可以指定文件系统的(虽然不建议这么做,CentOS 7的parted版本中已经取消了该功能),但在分区后格式化前,一般是比较难查看该分区的文件系统类型的,但使用file可以查看到。

[root@server1 ~]# file -s /dev/sda1
/dev/sda1: Linux rev 1.0 ext4 filesystem data (needs journal recovery) (extents) (huge files)

[root@server1 ~]# file -s /dev/sda
/dev/sda: x86 boot sector; GRand Unified Bootloader, stage1 version 0x3, boot drive 0x80, 1st sector stage2 0x7f86, GRUB version 0.94; partition 1: ID=0x83, active, starthead 32, startsector 2048, 512000 sectors; partition 2: ID=0x83, starthead 254, startsector 514048, 37332992 sectors; partition 3: ID=0x82, starthead 254, startsector 37847040, 4096000 sectors, code offset 0x48

1.6 文件/目录复制和移动

1.6.1 cp命令

cp [-apdriulfs] src dest # 复制单文件或单目录

cp [-apdriuslf] src1 src2 src3......dest_dir # 复制多文件、目录到一个目录下

选项说明:

-p: 文件的属性(权限、属组、时间戳)也复制过去。如果不指定p选项,谁执行复制动作,文件所有者和组就是谁。

-r或-R:递归复制,常用于复制非空目录。

-d:复制的源文件如果是链接文件,则复制链接文件而不是指向的文件本身。即保持链接属性,复制快捷方式本身。如果不指定-d,则复制的是链接所指向的文件。

-a:a=pdr三个选项。归档拷贝,常用于备份。

-i:复制时如果目标文件已经存在,询问是否替换。

-u:(update)若目标文件和源文件同名,但属性不一样(如修改时间,大小等),则覆盖目标文件。

-f:强制复制,如果目标存在,不会进行-i选项的询问和-u选项的考虑,直接覆盖。

-l:在目标位置建立硬链接,而不是复制文件本身。

-s:在目标位置建立软链接,而不是复制文件本身(软链接或符号链接相当于windows的快捷方式)。

一般使用cp -a即可,对于目录加上-r选项即可。

注意,bash内置命令在进行通配符匹配文件的时候,"*"、"?"、"[]"默认是无法匹配到以"."开头的文件的,所以"*"不会匹配隐藏文件。要通配隐藏文件,使用"."代替上述几种通配元字符即可,它能匹配除了"."和".."这两个特殊目录外的所有文件。

例如,复制/etc/skel目录下所有文件包括隐藏文件到/tmp目录下。

cp -a /etc/skel/. /tmp

如果有重复文件,则即使加上-f选项,也一样会交互式询问。解决方法可以是使用"yes"这个工具,它会不断的生成y字母直到进程被杀掉,当然也可以自行指定要生成的字符串。

yes | cp -a /etc/skel/. /tmp

删除/tmp下所有文件包括隐藏文件。

rm -rf /tmp/.*

还需要说明的是,对于非bash内置命令,通配方法可能不一样,有些命令有自己的通配标准,如新版本的find中的"*"可以匹配点开头的文件,但如果它采用的仍然是bash的通配方式,则和上面的匹配方法一样。

1.6.2 scp命令

scp是基于ssh的安全拷贝命令(security copy),它是从古老的远程复制命令rcp改变而来,实现的是在host与host之间的拷贝,可以是本地到远程的、本地到本地的,甚至可以远程到远程复制。注意,scp可能会询问密码。

如果scp拷贝的源文件在目标位置上已经存在时(文件同名),scp会替换已存在目标文件中的内容,但保持其inode号。

如果scp拷贝的源文件在目标位置上不存在,则会在目标位置上创建一个空文件,然后将源文件中的内容填充进去。

之所以解释上面的两句,是为了理解scp的机制,scp拷贝本质是只是填充内容的过程,它不会去修改目标文件的很多属性,对于从远程复制到另一远程时,其机制见后文。

scp [-12BCpqrv] [-l limit] [-o ssh_option] [-P port] [[user@]host1:]file1 ... [[user@]host2:]file2

选项说明:

-1:使用ssh v1版本,这是默认使用协议版本

-2:使用ssh v2版本

-C:拷贝时先压缩,节省带宽

-l limit:限制拷贝速度,Kbit/s.

-o ssh_option:指定ssh连接时的特殊选项,一般用不上。偶尔在连接过程中等待提示输入密码较慢时,可以设置GSSAPIAuthentication为no

-P port:指定目标主机上ssh端口,大写的字母P,默认是22端口

-p:拷贝时保持源文件的mtime,atime,owner,group,privileges

-r:递归拷贝,用于拷贝目录。注意,scp拷贝遇到链接文件时,会拷贝链接的源文件内容填充到目标文件中(scp的本质就是填充而非拷贝)

-v:输出详细信息,可以用来调试或查看scp的详细过程,分析scp的机制

示例:

1.把本地文件/home/a.tar.tz拷贝到远程服务器192.168.0.2上的/home/tmp,连接时使用远程的root用户:

scp /home/a.tar.tz root@192.168.0.2:/home/tmp/

2.目标主机不写路径时,表示拷贝到对方的家目录下:

scp /home/a.tar.tz root@192.168.0.2

3.把远程文件/home/a.tar.gz拷贝到本机:

scp root@192.168.0.2:/home/a.tar.tz # 不接本地目录表示拷贝到当前目录

scp root@192.168.0.2:/home/a.tar.tz /tmp # 拷贝到本地/tmp目录下

4.拷贝远程机器的/home/目录到本地/tmp目录下。

scp -r root@192.168.0.2:/home/ /tmp

5.从远程主机192.168.100.60拷贝文件到另一台远程主机192.168.100.62上。

scp root@192.168.100.60:/tmp/copy.txt root@192.168.100.62:/tmp

在远程复制到远程的过程中,例如在本地执行scp命令将A主机(192.168.100.60)上的/tmp/copy.txt复制到B主机(192.168.100.62)上的/tmp目录下,如果使用-v选项查看调试信息的话,会发现它的步骤类似是这样的。

# 以下是从结果中提取的过程

# 首先输出本地要执行的命令

Executing: /usr/bin/ssh -v -x -oClearAllForwardings yes -t -l root 192.168.100.60 scp -v /tmp/copy.txt root@192.168.100.62:/tmp

# 从本地连接到A主机

debug1: Connecting to 192.168.100.60 [192.168.100.60] port 22.

debug1: Connection established.  

# 要求验证本地和A主机之间的连接

debug1: Next authentication method: password

root@192.168.100.60‘s password:

# 将scp命令行修改后发送到A主机上

debug1: Sending command: scp -v /tmp/copy.txt root@192.168.100.62:/tmp

# 在A主机上执行scp命令

Executing: program /usr/bin/ssh host 192.168.100.62, user root, command scp -v -t /tmp

# 验证A主机和B主机之间的连接

debug1: Next authentication method: password

root@192.168.100.62‘s password:

# 从A主机上拷贝源文件到最终的B主机上

debug1: Sending command: scp -v -t /tmp

Sending file modes: C0770 24 copy.txt

Sink: C0770 24 copy.txt

copy.txt 100% 24 0.0KB/s  

# 关闭本地主机和A主机的连接

Connection to 192.168.100.60 closed.

也就是说,远程主机A到远程主机B的复制,实际上是将scp命令行从本地传递到主机A上,由A自己去执行scp命令。也就是说,本地主机不会和主机B有任何交互行为,本地主机就像是一个代理执行者一样,只是帮助传送scp命令行以及帮助显示信息。

其实从本地主机和主机A上的~/.ssh/know_hosts文件中可以看出,本地主机只是添加了主机A的信息,并没有添加主机B的信息,而在主机A上则添加了主机B的信息。

技术分享

1.6.3 mv命令

mv命令移动文件和目录,还可以用于重命名文件或目录。

mv [-iuf] src dest # 移动单个文件或目录

mv [-iuf] src1 src2 src3 dest_dir # 移动多个文件或目录

选项说明:

--backup[=CONTROL]:如果目标文件已存在,则对该文件做一个备份,默认备份文件是在文件名后加上波浪线,如/b.txt~

-b:类似于--backup,但不接受参数, 默认备份文件是在文件名后加上波浪线,如/b.txt~

-f:如果目标文件已存在,则强制覆盖文件

-i:如果目标文件已存在,则提示是否要覆盖,这是alias mv的默认选项

-n:如果目标文件已存在,则不覆盖已存在的文件

     如果同时指定了-f/-i/-n,则后指定的生效

-u:(update)如果源文件和目标文件不同,则移动,否则不移动

mv默认已经是递归移动,不需要-r参数。

1.6.4 mv的一个经典问题(mv的本质)

该问题涉及文件系统操作文件的机制,若不理解,请先深入学习文件系统。

mv不能实现里层同名目录覆盖外层同名目录。如/tmp下有a目录,a目录里还有a目录,将不能实现/tmp/a/a移动到/tmp。

[root@toystory tmp]# tree -L 3 a -fC

a
└── a/a
├── a/a/a
2 directories, 1 file

[root@toystory tmp]# mv a/* .
mv: overwrite `./a‘? y
mv: cannot move `a/a‘ to `./a‘: Directory not empty
 
[root@toystory tmp]# mv -f /tmp/a/* /tmp
mv: cannot move `/tmp/a/a‘ to `/tmp/a‘: Directory not empty

要解释为何会如此,先说明移动和覆盖动作的本质。

同文件系统下移动文件实际上是修改目标文件所在目录的data block,向其中添加一行指向inode table中待移动文件的inode的指针,如果目标路径下有同名文件,则会提示是否覆盖,实际上是覆盖指向该同名文件的inode指针,由于同名文件的inode记录指针被覆盖,就无法再找到该文件的data block,所以该文件被标记为删除。

跨文件系统移动文件的本质:如果目标路径下没有同名文件,则先为此文件分配一个inode号,并在目标目录的data block中添加一条指向该inode号的新记录(是全新的),然后将文件复制到目标位置,复制成功则删除源文件,复制失败则保留源文件;如果目标路径下有同名文件,则提示是否要覆盖,如果选择覆盖,则将该同名文件的inode指针指向新分配的inode号,然后将文件复制到目标位置,复制成功则删除源文件,复制失败则保留源文件。

技术分享

 

也就是说,同文件系统下移动文件时,inode记录不变(如inode号),当然,时间戳是一定会改变的,因为移动过程中修改了inode指向data block的指针。而跨文件系统下移动文件时,inode记录完全改变,它是新添加的记录。

再考虑上面的问题,同文件系统下移动文件时先在目标位置/tmp的data block中添加一条记录,如果同名则提示覆盖,覆盖时会先删除/tmp的data block中的a对应的记录,再添加将要移动文件的记录。从上面的结果也可以看出是先提示覆盖再提示目录非空的错误。

设想下,如果/tmp/a/a移动到/tmp下并重命名为b,则其动作是直接向/tmp的data block中添加b的记录,如果此时正好/tmp下已有b目录,则先删除/tmp的data block中b目录对应的记录,再添加移动后的b记录。

但是现在不是重命名为b,而是覆盖/tmp/a,此时的动作按原理应该是先提示是否覆盖,如果是,则删除/tmp的data block中a对应的记录,但由于此时/tmp/a目录中还有文件,该记录无法删除(因为如果要删除了该记录,代表删除了/tmp/a整个目录,而删除整个/tmp/a目录需要删除里面所有的文件,在删除它们之前的一个动作是把/tmp/a中的所有目录和文件的inode号标记为未使用,但此刻要移动的源目录/tmp/a/a是在使用当中的),所以提示目录非空而无法删除,这里所指的非空目录指的是/tmp/a,而非是/tmp/a/a非空。

但是在Windows操作系统下,里层目录是可以直接覆盖外层同名目录的,这和文件系统的行为有关。

其实在这个问题中,可以看出mv的很多原理。

1.7 查看文件内容

1.7.1 cat命令

输出一个或多个文件的内容。

cat [OPTION]... [FILE]...

选项说明

-n:显示所有行的行号

-b:显示非空行的行号

-E:在每行行尾加上$符号

-T:将TAB符号输出为"^I"

-s:压缩连续空行为单个空行

cat还有一个重要功能,允许将分行键入的内容输入到一个文件中去。

首先测试<<eof,这表示将键入的内容追加到标准输入stdin中(不是从标准输入中读取), eof可以随便使用其他符号代替。

[root@xuexi tmp]# cat <<eof
> abc.com
> eof
abc.com

再测试<eof,发现没有输入的机会,并且此时只能使用eof作为符号,EOF或其他任何都不可以。因为<eof是直接覆盖stdin,也就是没有输入。所以一定要使用<<eof。

[root@xuexi tmp]# cat <eof
[root@xuexi tmp]# cat <eox
-bash: eox: No such file or directory

[root@xuexi tmp]# cat <EOF
-bash: EOF: No such file or directory

再进一步测试<<eof的功能,将键入的内容重定向到文件而非标准输入中。这时有两种书写方案:

第一种方案:>>filename<<eof或>filename<<eof

[root@xuexi ~]# cat >>/tmp/test.txt<<EOF # 输入到这里按回车键继续输入下一行
> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # 按回车输入下一行
> yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy # 按回车输入下一行
> zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz # 按回车输入下一行
> EOF # 顶格写EOF结束输入

第二种方案:<<eof>filename或<<eof>>filename

[root@xuexi tmp]# cat <<eof>log.txt
> abc.com
> eof

两种方案结果是一样的,且总是使用<<eof,只不过所写的位置不同而已,不管写在哪个位置,它都表示将键入的内容追加到标准输入。然后再使用>filename或>>filename控制重定向的方式,将标准输入中的内容重定向到filename文件中。

1.7.2 tac

tac和cat字母正好是相反的,其作用也是和cat相反的,它会反向输出行,将最后一行放在第一行的位置输出,依此类推。但是,tac没有显示行号的参数。

shell> echo -e 1\n2\n3\n4\n5 | tac
5
4
3
2
1

1.7.3 head

head打印前面的几行。

head [-n num] | [-num] [-v] filename

-n:显示前num行;如果num是负数,则显示除了最后|num|(绝对值)行的其余所有行,即显示前"总行数-|num|"

-v:会显示出文件名

-n num是显示文件的前num行,num可以是+/-或不加正负号的整数,如果是正整数或不写+号,则显示前num行。如果是负整数,则从后向前数num行,并打印除了这些行的前面所有的行,即打印除了最后num行的所有行,也即总行数减num的前正数行。不写-n时默认是前10行。正整数时"-n num"可以直接简写"-num"。

不管怎么样,它取的都是前几行,哪怕是负整数也是前几行。

示例:

[root@xuexi ~]# echo -e 1\n2\n3\n4\n5 | head     # 取出默认前10行,但总共才有5行。
1
2
3
4
5

[root@xuexi ~]# echo -e 1\n2\n3\n4\n5 | head -2     # 取出前2行
1
2
或者
[root@xuexi ~]# echo -e 1\n2\n3\n4\n5 | head -n 2 # 取出前2行
1
2

[root@xuexi ~]# echo -e 1\n2\n3\n4\n5 | head -n -1 # 取出前5-1=4行
1
2
3
4

1.7.4 tail

tail和head相反,是显示后面的行,默认是后10行。

tail [OPTION]... [FILE]...

选项说明:

-n:输出最后num行,如果使用-n +num则表示输出从第num行开始的所有行

-f:监控文件变化

--pid=PID:和-f一起使用,在给定PID的进程死亡后,终止文件监控

-v:显示文件名

"-n -num"或"-num"或"-n num"(num为正整数)表示输出最后的num行。使用"-n +num"(num为正整数)则表示输出从第num行开始的所有行。

[root@xuexi ~]# echo -e 1\n2\n3\n4\n5 | tail -3         # 等价于 tail -n 3和tail -n -3
3
4
5

[root@xuexi tmp]# seq 6 | tail -n +3       # 打印除了前3-1=2行的所有行
3
4
5
6

tail还有一个重要的参数-f,监控文件的内容变化。当一个用户不断修改某个文件的尾部,另一个用户就可以通过这个命令来刷新并显示这些修改后的内容。

1.7.5 nl

以行号的方式查看内容。

常用"-b a",表示不论是否空行都显示行号,等价于cat -n;不写选项时,默认"-b t",表示空行不显示行号,等价于cat -b。

[root@xuexi ~]# nl /etc/issue # 默认空行不显示行号
1 CentOS release 6.6 (Final)
2 Kernel \r on an \m

[root@xuexi ~]# nl -b a /etc/issue
1 CentOS release 6.6 (Final)
2 Kernel \r on an \m
3

1.7.6 more和less

按页显示文件内容。使用more时,使用/搜索字符串,按下n或N键表示向下或向上继续搜索。使用less时,还多了一个搜索功能,使用?搜索字符串,同样,使用n或N键可以向上或向下继续搜索。

1.7.7 比较文件内容

shell> diff file1 file2

vimdiff file1 file2

1.8 文件查找类命令

1.8.1 which

显示命令或脚本的全路径,默认也会将命令的别名显示出来。

shell> which mv

alias mv=mv -i
       /bin/mv

1.8.2 whereis

找出二进制文件、源文件和man文档文件。

shell> whereis cd

cd: /usr/bin/cd /usr/share/man/man1/cd.1.gz /usr/share/man/man1p/cd.1p.gz /usr/share/man/mann/cd.n.gz

1.8.3 whatis

列出给定命令(并非一定是命令)的man文档信息。

shell> whatis passwd

sslpasswd (1ssl) - compute password hashes
passwd (1) - update users authentication tokens
passwd (5) - password file

根据上面的结果,执行:

man 1 passwd # 获取passwd命令的man文档

man 5 passwd # 获取password文件的man文档,文件类的man文档说明的是该文件中各配置项意义

man sslpasswd # 获取sslpasswd命令的man文档,实际上是openssl passwd的man文档

1.8.4 locate

没什么好说的。

1.8.5 find

内容太多,所以只给几个简单的示例。至于它详细的用法和它的运行机制,使用一篇单独的文章解释。

(1). 最基础的打印操作

find命令默认接的命令是-print,它默认以\n将找到的文件分隔。可以使用-print0来使用\0分隔,这样就不会分行了。但是一定要注意,-print0针对的是\n转\0,如果查找的文件名本身就含有空格,则find后-print0仍然会显示空格文件。所以-print0实现的是\n转\0的标记,可以使用其他工具将\0标记替换掉,如xargs,tr等。

[root@xuexi tmp]# mkdir /tmp/a

[root@xuexi tmp]# touch /tmp/a/{1..5}.log

[root@xuexi tmp]# find /tmp/a        # 等价于find /tmp/a -print,表示搜索/tmp/a目录
/tmp/a
/tmp/a/4.log
/tmp/a/2.log
/tmp/a/5.log
/tmp/a/1.log
/tmp/a/3.log

[root@xuexi tmp]# find /tmp/a -print0
/tmp/a/tmp/a/4.log/tmp/a/2.log/tmp/a/5.log/tmp/a/1.log/tmp/a/3.log

(2). 文件名搜索:-name或-path

-name可以对文件的basename进行匹配,-path可以对文件的dirname+basename。查找的文件名最好使用引号包围,可以配合通配符进行查找。

[root@xuexi tmp]# find /tmp -name "*.log"
/tmp/screen.log
/tmp/x.log
/tmp/timing.log
/tmp/a/4.log
/tmp/a/2.log
/tmp/a/5.log
/tmp/a/1.log
/tmp/a/3.log
/tmp/b.log

但不能在-name的模式中使用"/",除非文件名中包含了字符"/",否则将匹配不到任何东西,因为-name只对basename进行匹配。例如,想要匹配/tmp目录下某包含字符a的目录下的log文件。

shell> find /tmp -name *a*/*.log
find: warning: Unix filenames usually dont contain slashes (though pathnames do). That means that -name ‘*a*/*.log’‘ will probably evaluate to false all the time on this system. You might find the ‘-wholename‘ test more useful, or perhaps ‘-samefile‘. Alternatively, if you are using GNU grep, you could use ‘find ... -print0 | grep -FzZ ‘*a*/*.log’.

所以想要在指定目录下搜索某目录中的某文件,应该使用-path而不是-name。

[root@server2 tmp]# find /tmp -path *a*/*.log
/tmp/abc/axyz.log

注意,配合通配符[]时应该注意是基于字符顺序的,大小写字母的顺序是a-z --> A-Z,指定[a-z]表示小写字母a-z,同理[A-Z],而[a-zA-Z]和[a-Z]都表示所有大小写字母。当然还可以指定[a-A]表示a-z外加一个A。

字母的处理顺序较容易理解,关于数字的处理方法,见下面的示例。

[root@xuexi test]# ls
11.sh 1.sh 22.sh 2.sh 3.sh

[root@xuexi test]# find -name "[1-2].sh"
./2.sh
./1.sh

[root@xuexi test]# find -name "[1-23].sh"
./2.sh
./3.sh
./1.sh

[root@xuexi test]# touch 0.sh

[root@xuexi test]# find -name "[1-20].sh"
./2.sh
./0.sh
./1.sh

[root@xuexi test]# find -name "[1-22-3].sh"
./2.sh
./3.sh
./1.sh

从上面结果可以看出,其实[]只能匹配单个字符,[0-9]表示0-9的数字,[1-20]表示[1-2]外加一个0,[1-23]表示[1-2]外加一个3,[1-22-3]表示[1-2]或[2-3],迷惑点就是看上去是大于10的整数,其实是两个或者更多的单个数字组合体。也可以用这种方法表示多种匹配:[1-2,2-3]。

(3). 根据文件类型搜索:-type

一般需要搜索的文件类型就只有普通文件(f),目录(d),链接文件(l)。

例如,搜索普通文件类的文件,且名称为a开头的sh文件。

[root@xuexi test]# find /tmp -type f -name "a*.sh"

搜索目录类文件,且目录名以a开头。

[root@xuexi test]# find /tmp -type d -name "a*"

(4). 根据文件的时间戳搜索:-atime/-mtime/-ctime

例如搜索/tmp下3天内修改过内容的sh文件,因为是文件内容,所以不考虑搜索目录。

find /tmp -type f -mtime -3 -name "*.sh"

至于为什么是"-3",见find理论部分内容。

(5). 根据文件大小搜索:-size

例如搜索/tmp下大于100K的sh文件。

find /tmp -type f -size +100k -name *.sh

(6). 根据权限搜索:-perm

例如搜索/tmp下所有者具有可读科协可执行权限的sh文件。

find /tmp -type f -perm -0700 -name *.sh

(7). 搜索空文件

空文件可以是没有任何内容的普通文件,也可以是没有任何内容的目录。

例如搜索目录中没有文件的空目录。

find /tmp -type d -empty

(8). 搜索到文件后并删除

例如搜索到/tmp下的".tmp"文件然后删除。

find /tmp -type f -name "*.tmp" -exec rm -rf {} \;

但是这是极不安全的方法,因为如果文件名有空白字符的话,会造成误删除,例如文件名为"a xy.tmp",则直接-exec rm -rf ‘{}‘将会删除a和xy.tmp和"a xy.tmp",也就是说a这个文件或目录被误删除了。

想要完全掌握find,点find 。

 

 

 

 

第一章 文件类基础命令