CentOS-7 文件查找

643 阅读4分钟

which

作用:显示命令的绝对路径,which 只会去系统环境变量定义的目录中去查找文件。

选项说明
-a默认在 PATH 路径中由前向后查找命令,如果查找到了,就停止匹配。使用 -a 选项将遍历所有 PATH 路径,输出所有匹配项。
$ which cat
/usr/bin/cat

# which 还可以显示命令别名信息
$ which ls
alias ls='ls --color=auto'
  /usr/bin/ls
  
# shell 内置命令无法使用 which 查找
$ which for
/usr/bin/which: no for in (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin)

whereis

作用:显示命令及其帮助手册文件的绝对路径,或者文件的绝对路径。

选项说明
-b查找可执行文件
-m查找 man 手册
-s查找源代码文件
$ whereis cat
cat: /usr/bin/cat /usr/share/man/man1/cat.1.gz

$ whereis hosts
hosts: /etc/hosts /etc/hosts.allow /etc/hosts.deny

# 文件名:后面什么都没有,代表没有查找到
$ whereis ifcfg-eth0
ifcfg-eth0:

# -b:查找可执行文件
$ whereis -b cat
cat: /usr/bin/cat

# -m:查找帮助手册文件
$ whereis -m cat
cat: /usr/share/man/man1/cat.1.gz

# -s:查找源代码文件
$ whereis -s cat
cat:

locate & updatedb

作用:快速定位文件路径。

在 Linux 系统中有一个名为 mlocate.db 的文件,其中存储了系统文件的路径信息,locate 命令不是去磁盘中查找文件,而是直接读取 mlocate.db 这个文件的信息,找到文件的路径。此命令默认没有安装。

命令选项说明
locate-c只打印匹配到的行数,不打印匹配到的文件信息
locate-i匹配时忽略大小写
locate-r支持基本正则表达式
locate--regex支持扩展正则表达式
updatedb-U更新指定目录下的文件路径信息到数据库文件,默认更新系统全部文件
updatedb-v显示命令执行的过程
# 安装命令对应的软件包
$ yum install -y mlocate

# 手动更新 mlocate.db 中的信息
# 此数据库会由系统定期自动更新
$ updatedb

$ locate hosts
/etc/hosts
/etc/hosts.allow
/etc/hosts.deny
/etc/selinux/targeted/active/modules/100/denyhosts

# 支持通配符
$ locate /etc/sh*
/etc/shadow
/etc/shadow-
/etc/shells

# -c:只打印匹配到的行数,不打印匹配到的文件信息
~ locate -c hosts
24

# -U:更新指定目录下的文件路径信息到数据库文件
# -v:显示命令执行的过程
~ updatedb -vU /etc

find

作用:查找文件

命令格式:

find命令格式.png

【如何处理符号链接】

-H-L-P-D debugopts-Olevel 这些选项,暂时不做介绍;

【path】

查找的目录,比如 . 代表当前目录,/ 代表系统根目录;

【options】

选项说明
-depth从指定目录下最深层的子目录开始查找
-maxdepth level查找的最大目录层数,level 是非负整数
-regextype type改变正则表达式的模式,默认为 emacs,还有 posix-awk、posix-basic、posix-egrep、posix-extended

【tests】

选项说明
-mtime [-n|n|+n]按照文件的修改时间来查找文件,单位天
-n:文件修改时间距现在 n 天以内
+n:文件修改时间距现在 n 天之前
n:文件修改时间距现在 n 天
-atime [-n|n|+n]按照文件的访问时间来查找,单位天
-ctime [-n|n|+n]按照文件的创建时间来查找,单位天
-amin按照文件的访问时间来查找,单位分钟
-cmin按照文件的创建时间来查找,单位分钟
-group按照文件所属的组查找
-name按照文件名查找,只支持 * ? [] 等通配符
-iname按照文件名查找,只支持 * ? [] 等通配符,忽略大小写
-newer查找修改时间比指定文件的修改时间新的文件
-nogroup查找没有有效用户组的文件,即该文件的组在 /etc/groups 文件中不存在
-nouser查找没有有效用户的文件,即该文件的用户在 /etc/passwd 文件中不存在
-path pattern指定路径,配合 -prune 参数排除指定目录
-perm按照文件权限来查找
-regex接正则表达式
-iregex接正则表达式,不区分大小写
-size n[cwbkMG]查找文件长度为 n 块的文件,带有 cwbkMG 时表示文件长度按照字节计算,默认单位 b
c:1 byte
w:2 bytes
b:512 bytes
k:1024 bytes
M:1024 * 1024 bytes
G:1024 * 1024 * 1024 bytes
-size +100M 查找文件大小大于 100M 的文件
-size 100M 查找文件大小等于 100M 的文件
-size -100M查找文件大小小于 100M 的文件
-user按照文件用户来查找
-type查找指定类型的文件
b:块设备文件
c:字符设备文件
d:目录
p:管道文件
l:链接文件
f:普通文件
s:socket 文件
D:door(Solaris)

【actions】

选项说明
-print将匹配到的文件打印出来,默认
-delete删除匹配到的文件
-exec对匹配到的文件执行该参数所给出的命令
-ok同 -exec,但是会询问用户是否执行
-prune使 find 命令不在指定的目录中查找

find 支持【逻辑运算符】

逻辑运算符说明
!取反
-a取交集,and
-o取并集,or

【案例】

# 在 /var/log/ 目录下查找修改时间距离现在大于4天的以".log"结尾的文件
$ find /var/log -mtime +5 -name "*.log"

# 在当前目录下查找在两天内受过访问的文件
$ find . -atime -2

# 在当前目录下查找在两天内被修改过的文件
$ find . -mtime -5

# 查找当前目录下的所有目录
$ find . -type d

# 查找当前目录下不是目录的文件
$ find . ! -type d

# 查找/etc目录下权限为755的文件
$ find /etc -perm 755

# 查找组权限是 w,其他权限都没有的文件,即查找权限为 0020 的文件
$ find /etc -perm g=w

# 查找组权限是 w,其他权限任意的文件
$ find /etc -perm -g=w

# 查找权限为 664 的文件
$ find . -perm 664

# 查找用户和组具有读写权限,others具有读权限的文件,权限大于 664 的文件也满足条件
$ find . -perm -664

# 查找文件的属主【或者】组内成员【或者】其他人可写的文件
$ find . -perm /222

# 查找文件的属主【或者】组内成员可写的文件
$find . -perm /220

# 查找文件的属主【或者】组内成员可写的文件
$ find . -perm /u+w,g+w

# 查找文件的属主【或者】组内成员可写的文件
find . -perm /u=w,g=w

# 查找文件的属主和组内成员【都】可写的文件
$ find . -perm -220

# 查找文件的属主和组内成员【都】可写的文件
$ find . -perm -g+w,u+w

# 查找所有人都可读【and】属主、组内成员以及其他人至少有一个可以写入【and】 属主、组内成员以及其他人至少有一个不能执行的文件
$ find . -perm -444 -perm /222 ! -perm /111

# 查找所有人都可读【and】属主、组内成员以及其他人至少有一个可以写入【and】 属主、组内成员以及其他人至少有一个不能执行的文件
# u(user),g(group),o(other),a(all)
$ find . -perm -a+r -perm /a+w ! -perm /a+x

# 查找当前目录下文件大小大于 1000 字节的文件
$ find . -size +1000c

# 查找 /etc 目录下的所有文件,除了/etc/ssh 目录
$ find /etc -path /etc/ssh -a -prune -o -print
# 命令解释
# 在目录 /etc 下查找所有文件
# 如果查找到目录 /etc/ssh,就执行 -prune 选项,即忽略
# 否则就打印出来
# 以上命令可以简写(省略 -a 选项)为:
$ find /etc -path /etc/ssh -prune -o -print

# 忽略多个目录
# ()需要转义
# 注意左右括号和 -path 之间的空格是必须的
$ find /etc \( -path /etc/ssh -o -path /etc/python \) -prune -o -print

# 在当前目录下查找 root 用户的文件
$ find . -user root

# 在当前目录下查找无效用户的文件
$ find . -nouser

# 在当前目录下查找用户组为 nobody 的文件
$ find . -group nobody

# 修改用户属组为555
$ chown .555 file.txt
# 在当前目录下查找没有有效用户组的文件 
$ find . -nogroup

# 在当前目录下查找修改时间比文件 file.txt 新的文件
$ find . -newer file.txt

# 在当前目录下查找修改时间比 file1.txt 新但比 file2.txt 旧的文件
$ find . -newer file1.txt ! -newer file2.txt

# 指定查找深度
~ tree
.
├── a
│   ├── a.txt
│   └── b
│       └── a.txt
└── a.txt

$ find . -maxdepth 1 -name a.txt
./a.txt
$ find . -maxdepth 2 -name a.txt
./a.txt
./a/a.txt
$ find . -maxdepth 3 -name a.txt
./a.txt
./a/b/a.txt
./a/a.txt

# 查询深度为 1 并且文件类型为目录并且目录名不是"."的文件
$ find . -maxdepth 1 -type d ! -name "."

# 查询 深度为1 && ((文件类型是目录 && 目录名不是".") || 文件名是"a.txt") 的文件
$ find . -maxdepth 1 -type d ! -name "." -o -name "a.txt"

# 在根目录下查找名为 "find" 的文件
$ find / -regex "find"

# 在根目录下查找文件名中包含 "find" 字符串的文件
$ find / -regex ".*find"

# 指定正则表达式的类型
$ find . -regextype "posix-egrep" -name '*[0-9]'

# find命令的结果给到其他命令
# 命令格式:find path options -exec command {} \;
# 其中 {} \; 是固定写法
# {} 代表 find 命令执行后的所有文件
$ find . -type f -exec ls -l {} \;

# 删除修改时间在14天之前的文件
$ find . -type f -mtime +14 -exec rm {} \;
# 删除修改时间在14天之前的文件,删除之前逐个询问是否删除,输入 y 代表删除,输入 n 代表不删除
$ find . -type f -mtime +14 -ok rm {} \;

# 删除匹配到的文件
$ find . -name "a.txt" -delete

xargs

作用:将标准输入转换成命令行参数

选项说明
-n指定每行最大参数个数 n,可以将标准输入的文本划分为多行,每行 n 个参数,默认空格分隔
-d自定义分隔符
-i以{}代表前面的结果
-I指定一个符号代表前面的结果,而不是使用 -i 默认的 {}
-p提示让用户确认是否执行后面的命令,y 执行,n 不执行
-0用 null 代替空格作为分隔符,配合 find 命令的 -print 选项的输出使用
$ cat a.txt 
1 2 3 4 5 6
7 8 9
$ xargs < a.txt 
1 2 3 4 5 6 7 8 9

# -n:指定每行的输出
$ xargs -n 3 < a.txt
1 2 3
4 5 6
7 8 9

# -d:自定义分隔符
$ echo aXaXaX
aXaXaX
$ echo aXaXaX |xargs -d X
a a a 
$ echo aXaXaX |xargs -d X -n 2
a a
a

# 查找文件并显示详细属性
$ find . -type f | xargs ls -l

# 测试 -I 选项
$ touch file{1..9}.log
$ mkdir dir1
$ find . -name "file*log" | xargs -i mv {} dir1
$ ls dir1
file1.log  file2.log  file3.log  file4.log  file5.log  file6.log  file7.log  file8.log  file9.log

$ mkdir dir2
$ find dir1 -name "file*log" | xargs -I [] cp [] dir2
$ ls dir2
file1.log  file2.log  file3.log  file4.log  file5.log  file6.log  file7.log  file8.log  file9.log

# 创建带有空格的文件
$ touch "hello world.txt"
# 或者
$ touch hello\ world.txt

# 删除"hello world.txt"
# xargs 默认认为空格是分隔符
$ find . -name "hello world.txt" | xargs rm
rm: cannot remove ‘./hello’: No such file or directory
rm: cannot remove ‘world.txt’: No such file or directory

# -0:设置分隔符为 null
# -print0:去除文件名后的换行符号
$ find . -name "hello world.txt" -print0 | xargs -0 rm

# -p:询问是否执行后面的命令,y 代表执行,n 代表不执行
$ find . -type f | xargs -p rm

find + xargs 的执行原理:

find . -type f | xargs ls -l

首先 find 命令找到一些符合条件的文件,比如:

a.txt
b.txt

然后 xargs 命令将找到的文件名转换行成一行内容:

a.txt b.txt

将转换后的一行内容加到 xargs 之后的命令之后:

ls -l a.txt b.txt

所以执行 find . -type f | xargs mv /dir 命令会报错,因为实际执行的命令是:

mv /dir a.txt b.txt

所以需要这样执行:

# 方法一:使用 xargs 的选项
find . -type f | xargs -i mv {} /dir

# 方法二:使用 xargs 的命令的选项
# mv -t /dir:指明目标目录是 /dir
find . -type f | xargs mv -t /dir

说明:

  • xargs 之后 执行的命令会使用原始命令而不使用别名,所以 xargs 之后的 rm 命令可以不加选项 -f

  • find命令结合exec和xargs使用的区别:

    • 执行效率:find 命令将查找到的文件逐个传递给 -exec 参数来执行,查到一个传递一个,如果查找到的文件较多,则执行效率较慢;xargs 命令将在 find 命令执行结束之后统一处理所有查找到的文件,效率较高,所以打包的时候,一定要使用 xargs 命令才能将多个文件打包在一起,否则包中只有最后一个文件;

    • 特殊字符处理:查找到的文件名中如果包含特殊字符比如空格,-exec 命令会照常处理;而使用 xargs 命令处理带特殊字符的文件,需要做特殊处理,例如:find . -name "hello world.txt" -print0 | xargs -0 ls -lh

      $ find . -name "hello world.txt" -print0 | xargs -0 ls -lh
      -rw-rw-r-- 1 admin admin 0 Jul  2 14:34 ./hello world.txt
      
      $ find . -name "hello world.txt" -exec ls -lh {} \;
      -rw-rw-r-- 1 admin admin 0 Jul  2 14:34 ./hello world.txt