Jade Dungeon

文件搜索

查找命令与程序:whereis

whereis的作用是用来定位命令的二进制文件、资源或者帮助页。举例来说,获得lskill命令的二进制文件/资源以及帮助页:

root@tecmint:~# whereis ls
ls: /bin/ls /usr/share/man/man1/ls.1.gz

root@tecmint:~# whereis kill
kill: /bin/kill /usr/share/man/man2/kill.2.gz /usr/share/man/man1/kill.1.gz

注意:当需要知道二进制文件保存位置时有用。

查找文件:find

限定目录深度

find会默认查找子目录。使用-mindepth-maxdepth限定搜索指定目录的深度。

root目录及其子目录下查找passwd文件。

find / -name passwd
# ./usr/share/doc/nss_ldap-253/pam.d/passwd
# ./usr/bin/passwd
# ./etc/pam.d/passwd
# ./etc/passwd

root目录及其1层深的子目录中查找passwd.

  1. root — level 1,
  2. sub-directory — level 2
find -maxdepth 2 -name passwd
# ./etc/passwd

root目录下及其最大两层深度的子目录中查找passwd文件.

find / -maxdepth 3 -name passwd
# ./usr/bin/passwd
# ./etc/pam.d/passwd
# ./etc/passwd

在第二层子目录和第四层子目录之间查找passwd文件。

find / -maxdepth 3 -name passwd
find -mindepth 3 -maxdepth 5 -name passwd
# ./usr/bin/passwd
# ./etc/pam.d/passwd

限制文件还是目录

限制文件还是目录:-type

  • 默认只查询文件相当于-type f选项
  • 要查找目录要声明-type d
  • 要查找socket文件要声明-type s

Find all directories whose name is Tecmint in / directory.

find / -type d -name Tecmint

Find all php files whose name is tecmint.php in a current working directory.

find . -type f -name tecmint.php

只查找socket文件

find . -type s

查找所有的隐藏文件

find . -type f -name ".*"

查找所有的隐藏目录

find -type d -name ".*"

如果我们查找的时候不想隐藏文件也显示出来,可以使用下面的正则式查找:

下面的命令会显示当前目录及其子目录下15分钟内文件内容被修改过的文件,并且只列出 非隐藏文件。也就是说,以.开头的文件时不会显示出来的

find . -mmin -15 \( ! -regex ".*/\..*" \)

-empty选项查找空目录

# To file all empty files under certain path.
find /tmp -type f -empty

# To file all empty directories under certain path.
find /tmp -type d -empty

# 下面命令的输出文件绝大多数都是锁定文件盒其他程序创建的place hoders
find ~ -empty

# 只列出你`home`目录里的空文件。
find . -maxdepth 1 -empty

# 只列出当前目录下的非隐藏空文件。
find . -maxdepth 1 -empty -not -name ".*"

限制在当前分区

限制在当前分区:-xdev

系统管理员有时候仅仅想在/挂载的文件系统分区上搜索,而不想去搜索其他的挂载分区 ,比如/home/挂载分区。如果你有多个分区被挂载了,你想在/下搜索,一般可以按 下面的这样做

下面这个命令会搜索根目录/及其子目录下所有.log结尾的文件名。如果你有多个分区 在/下面,那么这个搜索会去搜索所有的被挂载的分区:

find / -name "*.log"

如果我们使用-xdev选项,那么仅仅会在在当前文件系统中搜索。

下面的命令会在/目录及其子目录下搜索当前文件系统(也就是/挂载的文件系统)中所有 以.log结尾的文件,也就是说如果你有多个分区挂载在/下面,下面的搜索不会去搜索 其他的分区的(比如/home/

find / -xdev -name "*.log"

使用正则查找

正则方式查找.txt和pdf

find . -regex  ".*(.txt|.pdf)$"
#-iregex:忽略大小写的正则

按文件名查找

-name选项按文件名查询,使用*作为通配符。

# 当前目录找名为`tecmint.txt`:
find . -name tecmint.txt

# Find all the files under `/home` directory with name `tecmint.txt`.
find /home -name tecmint.txt

-iname选项按忽略文件名大小写。

# `/home`目录中找忽略大小写`tecmint.txt`
find /home -iname tecmint.txt

按inode编号查找

-inum标签使用inode编号查找文件

任何一个文件都有一个独一无二的inode编号,借此我们可以区分文件。创建两个名字 相似的文件,例如一个有空格结尾,一个没有。

touch "test-file-name"

touch "test-file-name "
[Note: There is a space at the end]

ls -1 test*
test-file-name
test-file-name

ls的输出不能区分哪个文件有空格结尾。使用选项-i,可以看到文件的inode编号 ,借此可以区分这两个文件。

ls -i1 test*
16187429 test-file-name
16187430 test-file-name

你可以如下面所示在find命令中指定inode编号。在此,find命令用inode编号 重命名了一个文件。

find -inum 16187430 -exec mv {} new-test-file-name \;

ls -i1 *test*
16187430 new-test-file-name
16187429 test-file-name

你可以在你想对那些像上面一样的糟糕命名的文件做某些操作时使用这一技术。例如,名为 file?.txt的文件名字中有一个特殊字符。若你想执行rm file?.txt,下面所示的所有 三个文件都会被删除。所以,采用下面的步骤来删除file?.txt文件。

ls
file1.txt  file2.txt  file?.txt

找到每一个文件的inode编号。

ls -i1
804178 file1.txt
804179 file2.txt
804180 file?.txt

如下所示: 使用inode编号来删除那些具有特殊符号的文件名。

find -inum 804180 -exec rm {} \;

ls
file1.txt  file2.txt
[Note: The file with name "file?.txt" is now removed]

根据权限查找

-perm指定权限。

# 查找权限是`777`的文件:
find . -type f -perm 0777 -print

# 查找权限不是`777`的文件:
find / -type f ! -perm 777

# Find all the SGID bit files whose permissions set to `644`.
find / -perm 2644

# Find all the Sticky Bit set files whose permission are `551`.
find / -perm 1551

# Find all SUID set files.
find / -perm /u=s

# Find all SGID set files.
find / -perm /g+s

# Find all Read Only files.
find / -perm /u=r

# Find all Executable files.
find / -perm /a=x

根据用户查找

-user选项按用户查找

# `/`下root用户的名为`tecmint.txt` 文件
find /  -user root -name tecmint.txt

find /home -user tecmint -iname "*.txt"

# `/home` 下属于Tecmint的文件 
find /home -user tecmint

-nouser选项按查找用户不存在的文件

为了查找属主帐户已经被删除的文件,可以使用-nouser选项。这样就能够找到那些属主 在/etc/passwd文件中没有有效帐户的文件。

在使用-nouser选项时,不必给出用户名,find命令能够为你完成相应的工作。 例如,希望在/home目录下查找所有的这类文件,可以用:

find /home -nouser

根据用户组查找

-group选项按用户组查找

To find all files that belongs to group Developer under /home directory.

find /home -group developer

-nogroup选项按用户组查找

要查找没有有效所属用户组的所有文件,可以使用nogroup选项。下面的find命令从文件 系统的根目录处查找这样的文件

fine / -nogroup

按时间查找

你可以找到基于以下三个文件的时间属性的文件。

  • 访问时间的文件。文件访问时,访问时间得到更新。
  • 的文件的修改时间。文件内容修改时,修改时间得到更新。
  • 更改文件的时间。更改时间时,被更新的inode数据的变化。

访问时间

  • -amin最近访问分钟查找
  • -atime最近访问天数查找
# To find all the files which are accessed in last 1 hour.
find / -amin -60

# To find all the files which are accessed 50 days back.
find / -atime 50

# 1天内被访问了的文件
find / -atime -1

修改内容时间

  • -mtime:文件内容修改过的天数
  • -mmin:文件内容修改过的分钟数

执行下面例子中的命令,将会找到当前目录以及其子目录下,最近一次修改时间在1个小时 (60分钟)之内的文件或目录:

find . -mmin -60

同样的方式,执行下面例子中的命令,将会找到24小时(1天)内修改了的文件(文件系统 根目录/下):

find / -mtime -1
# To find all the files which are modified 50 days back.
find / -mtime 50

# modified more than 50 days back and less than 100 days.
find / -mtime +50 –mtime -100

# modified in last 1 hour.
find / -mmin -60

修改状态时间

  • -cmin:文件属性修改过的天数
  • -ctim:文件属性修改过的分钟数
# changed in last 1 hour.
find / -cmin -60

对比修改时间

-newer选项对比其他文件更新时间查找

显示在指定文件之后做出修改的文件。下面的find命令将显示所有的在ordinary_file 之后创建修改的文件。

ls -lrt
total 0
-rw-r----- 1 root root 0 2009-02-19 20:27 others_can_also_read
----r----- 1 root root 0 2009-02-19 20:27 others_can_only_read
-rw------- 1 root root 0 2009-02-19 20:29 ordinary_file
-rw-r--r-- 1 root root 0 2009-02-19 20:30 everybody_read
-rwxrwxrwx 1 root root 0 2009-02-19 20:31 all_for_all
---------- 1 root root 0 2009-02-19 20:31 no_for_all
find -newer ordinary_file
.
./everybody_read
./all_for_all
./no_for_all

-cnewer选项查找状态改变时间在某个文件修改时间之后的文件

find -cnewer FILE

下面的例子显示在修改文件/etc/fstab之后所有文件状态改变过的文件。如果你在 /etc/fstab新增了一个挂载点,你很可能像知道那之后那些文件的状态发生了改变,这 时候你可以使用如下命令:

find -cnewer /etc/fstab

按大小查找

使用-size选项可以通过文件大小查找文件。指比给定尺寸小,+指比给定尺寸大。 没有符号代表和给定尺寸完全一样大。

find ~ -size +100M

# 查找比指定文件小的文件
find ~ -size -100M

# 查找符合给定大小的文件
find ~ -size 100M

# To find all 50MB files, use.
find / -size 50M

# To find all the files which are greater than 50MB and less than 100MB.
find / -size +50M -size -100M

查找5个最大的文件

下面的命令列出当前目录及子目录下的5个最大的文件。这会需要一点时间,取决于命令 需要处理的文件数量。

find . -type f -exec ls -s {} \; | sort -n -r | head -5

查找5个最小的文件

方法同查找5个最大的文件类似,区别只是sort的顺序是降序。

find . -type f -exec ls -s {} \; | sort -n  | head -5

上面的命令中,很可能你看到的只是空文件(0字节文件)。如此,你可以使用下面的命令 列出最小的文件,而不是0字节文件。

find . -not -empty -type f -exec ls -s {} \; | sort -n  | head -5

相反匹配

相反匹配:-not

# 否定参数:查找所有非txt文本
find . ! -name "*.txt" -print

显示所有的名字不是MyCProgram.c的文件或者目录。由于maxdepth是1,所以只会显示 当前目录下的文件和目录。

find -maxdepth 1 -not -iname "MyCProgram.c"
# .
# ./MybashProgram.sh
# ./create_sample_files.sh
# ./backup
# ./Program.c

组合条件

查找txt或pdf文件

find . ( -name "*.txt" -o -name "*.pdf" ) -print

删除找到的文件

找到后的后续动作删除:

# 删除当前目录下所有的swp文件:
find . -type f -name "*.swp" -delete

find 与 exec 组合

一旦find命令匹配到了相应的文件,就可以用-exec选项中的命令对其进行操作 (在有些操作系统中只允许-exec选项执行诸如lsls -l这样的命令)

命令格式:

find [文件路径] [find命令选项] [要查找的类型文件] -exec [执行的命令] {} \;

{}是一个特殊的字符串,对于每一个匹配的文件,{}会被替换成相应的文件名

当使用诸如mv或rm命令时,可以使用-ok选项的安全模式。 它将在对每个匹配到的文件进行操作之前提示你。

交互确认把-exec换成-ok命令格式:

find [文件路径] [find命令选项] [要查找的类型文件] -ok   [执行的命令] {} \;

例:

# 文件名以`.LOG`结尾、更改时间在5日以上的文件,并删除它们
# 删除之前先给出提示。
$ find . -name "*.LOG" -mtime +5 -ok rm {} \; 

# 将当前目录下的所有权变更为weber
find . -type f -user root -exec chown weber {} ; 

# 将找到的文件全都copy到另一个目录:
find . -type f -mtime +10 -name "*.txt" -exec cp {} OLD ;

在同一个命令中使用多个{}

linux手册说命令中只能使用一个{},不过你可以像下面这样在同一个命令中使用多个:

find -name "*.txt" cp {} {}.bkup \;

注意,在同一个命令中使用这个{}是可以的,但是在不同的命令里就不行了,也就是说, 如果你想象下面这样重命名文件是行不通的:

find -name "*.txt" -exec mv {} `basename {} .htm`.html \;

使用多个{}实例

你可以像下面这样写一个shell脚本去模拟上面那个重命名的例子

mv "$1" "`basename "$1" .htm`.html"

上面的双引号是为了防止文件名中出现的空格,不加的话会有问题。然后你把这个shell 脚本保存为mv.sh,你可以像下面这样使用find命令了:

find -name "*.html" -exec ./mv.sh '{}' \;

所以,任何情况下你在find命令执行中想使用同一个文件名多次的话,先写一个脚本,然后 在find中通过-exec执行这个脚本,把文件名参数传递进去就行,这是最简单的办法

将文件名中的空格换成下划线

你从网上下载下来的音频文件的文件名很多都带有空格。但是带有空格的文件名在linux (类Unix)系统里面是很不好的。你可以使用find然后后面加上rename命令的替换功能去 重命名这些文件,将空格转换成下划线_

$ find . -type f -iname "*.mp3" -exec rename "s/ /_/g" {} \;

在find结果中同时执行两条命令

在find的man page页面中,下面是一次文件查找遍历中使用两条命令的语法举例

下面的find命令的例子,遍历文件系统一次,列出拥有setuid属性的文件和目录,写入 /root/suid.txt文件, 如果文件大小超过100M,将其记录到/root/big.txt中:

find / \( -perm -4000 -fprintf /root/suid.txt '%#m %u %p\n' \) , \
 \( -size +100M -fprintf /root/big.txt '%-10s %p\n' \)

给常用find操作取别名

若你发现有些东西很有用,你可以给他取别名。并且在任何你希望的地方执行。

常用的删除a.out文件。

alias rmao="find . -iname a.out -exec rm {} \;"
rmao

删除c程序产生的core文件。

alias rmc="find . -iname core -exec rm {} \;"
rmc

删除大型打包文件

下面的命令删除大于100M*.zip文件。

find / -type f -name *.zip -size +100M -exec rm -i {} \;"

用别名rm100m删除所有大于100M的*.tar文件。使用同样的思想可以创建rm1g,rm2g ,rm5g的一类别名来删除所有大于1G,2G,5G的文件。

alias rm100m="find / -type f -name *.tar -size +100M -exec rm -i {} \;"
alias rm1g="find / -type f -name *.tar -size +1G -exec rm -i {} \;"
alias rm2g="find / -type f -name *.tar -size +2G -exec rm -i {} \;"
alias rm5g="find / -type f -name *.tar -size +5G -exec rm -i {} \;"

rm100m
rm1g
rm2g
rm5g

根据文件名删除

To find a single file called tecmint.txt and remove it.

find . -type f -name "tecmint.txt" -exec rm -f {} \;

To find and remove multiple files such as .mp3 or .txt, then use.

find . -type f -name "*.txt" -exec rm -f {} \;

OR

find . -type f -name "*.mp3" -exec rm -f {} \;

按大小查找删除文件

To find all 100MB files and delete them using one single command.

find / -size +100M -exec rm -rf {} \;

Find all .mp3 files with more than 10MB and delete them using one single command.

find / -type f -name *.mp3 -size +10M -exec ls -l {} \;

按权限查找并修改权限

找到权限为777的文件并修改权限为644

find / -type f -perm 0777 -print -exec chmod 644 {} \;

找到权限为777的目录并修改权限为755

find / -type d -perm 777 -print -exec chmod 755 {} \;

命令行参数转换:xargs

xrags命令与exec命令相似,都是在find匹配完文件后在执行相应的命令。不幸的是, 有些系统对能够传递给 exec的命令长度有限制,这样在find命令运行几分钟之后, 就会出现溢出错误。错误信息通常是“参数列太长”或“参数列溢出”。 这就是xargs命令的用处所在,特别是与 find命令一起使用。

Find命令把匹配到的文件传递给 xargs命令,而xargs命令每次只获取一部分文件而不是全部, 不像 -exec选项那样。这样它可以先处理最先获取的一部分文件,然后是下一批,并如此继续下去。 xargs能够将输入数据转化为特定命令的命令行参数;

  • 是多行文本间的定界符
  • 将单行转化为多行输出

这样,可以配合很多命令来组合使用。比如grepfind

xargs参数说明

  • -d:定义定界符 (默认为空格 多行的定界符为 )
  • -n:指定输出为多行
  • -I{} 指定替换字符串,这个字符串在xargs扩展时会被替换掉,用于待执行的命令需要多个参数时
$ cat grocery.list
apples
bananas
plums
carrots

$ 将多行输出转化为单行输出
$ cat grocery.list | xargs
apples bananas plums carrots

# -n:指定每行显示的字段数
$cat single.txt | xargs -n 3       # 每行显示三个字段 
apples bananas plums
carrots

cat file.txt | xargs -I {} ./command.sh -p {} -1

# -0:指定为输入定界符
# 统计程序行数
find source_dir/ -type f -name "*.cpp" -print0 |xargs -0 wc -l

查找文件并显示MD5特征

下面的例子展示了find命令来计算所有不区分大小写的文件名为MyCProgram.c的文件的 MD5验证和。

  • {}将会被当前文件名取代。
  • 那个\好像是类似转义用的,
    • 后面加上;表示命令会执行多次,每次后面都加上找到的一个文件。
    • 后面加上+,表示只会执行一次命令,执行的时候把所有找到的文件一次性传给它。
find -iname "MyCProgram.c" -exec md5sum {} \;
d41d8cd98f00b204e9800998ecf8427e  ./mycprogram.c
d41d8cd98f00b204e9800998ecf8427e  ./backup/mycprogram.c
d41d8cd98f00b204e9800998ecf8427e  ./backup/MyCProgram.c
d41d8cd98f00b204e9800998ecf8427e  ./MyCProgram.c

根据文件名删除文件

find /path -name mytestfile | xargs rm

grep

grep命令搜索指定文件中包含给定字符串或者单词的行。

grep [match_patten] [file]      # 默认访问匹配行

常用参数:

  • -o:只输出匹配的文本行 VS -v只输出没有匹配的文本行
  • -c用于统计满足要求的行(grep -c 'word' /path/to/file
grep -c "text" filename
  • n:显示匹配的行号
  • i:搜索时忽略大小写
  • l:只显示文件名

在多级目录中对文本递归搜索 (程序员搜代码的最爱):

# 举例搜索`/etc/passwd`文件中的`tecmint`
$ grep tecmint /etc/passwd
tecmint:x:1000:1000:Tecmint,,,:/home/tecmint:/bin/bash

# 在目录`src`及子目录中的`*.cpp`文件中查找`MyClass`
$ grep -R --include="*.cpp" "MyClass" "src"

匹配多个模式

grep -e "class" -e "vitural" file

-z:grep输出以作为结尾符的文件名:

grep "test" file* -lZ| xargs -0 rm
  • -h不显示文件名。
  • -v输出不匹配的行。
  • -w搜索单词(egrep -w 'word1|word2' /path/to/file
  • –color彩色输出(grep –color server /etc/passwd)
# 在多个文件中查找:
$ grep apple grocery.list grocery.list2
grocery.list:apples
grocery.list2:dry apples

# 使用`-i`选项将忽略大小写。
$ grep -i TECMINT /etc/passwd
tecmint:x:1000:1000:Tecmint,,,:/home/tecmint:/bin/bash

# 使用`-r`选项递归搜索所有自目录下包含字符串`127.0.0.1`的行。
$ grep -r "127.0.0.1" /etc/
/etc/vlc/lua/http/.hosts:127.0.0.1
/etc/speech-dispatcher/modules/ivona.conf:#IvonaServerHost "127.0.0.1"
/etc/mysql/my.cnf:bind-address      = 127.0.0.1
/etc/apache2/mods-available/status.conf:    Allow from 127.0.0.1 ::1
/etc/apache2/mods-available/ldap.conf:    Allow from 127.0.0.1 ::1
/etc/apache2/mods-available/info.conf:    Allow from 127.0.0.1 ::1
/etc/apache2/mods-available/proxy_balancer.conf:#    Allow from 127.0.0.1 ::1
/etc/security/access.conf:#+ : root : 127.0.0.1
/etc/dhcp/dhclient.conf:#prepend domain-name-servers 127.0.0.1;
/etc/dhcp/dhclient.conf:#  option domain-name-servers 127.0.0.1;
/etc/init/network-interface.conf:   ifconfig lo 127.0.0.1 up || true
/etc/java-6-openjdk/net.properties:# localhost & 127.0.0.1).
/etc/java-6-openjdk/net.properties:# http.nonProxyHosts=localhost|127.0.0.1
/etc/java-6-openjdk/net.properties:# localhost & 127.0.0.1).
/etc/java-6-openjdk/net.properties:# ftp.nonProxyHosts=localhost|127.0.0.1
/etc/hosts:127.0.0.1    localhost

例:单词匹配

$ grep -w apples grocery.list grocery.list2
grocery.list:apples
grocery.list2:dry apples

组合find与grep

find / -name "*.conf" | xargs grep "alias" 

ack

对于系统管理员或程序员来说,当需要在复杂配置的目录中或者在大型源码树中搜寻特定的文本或模式时,grep类型的工具大概是最受欢迎的。

如果grep是你最喜欢的工具之一,那么你可能会更喜欢ack。ack是一个基于Perl的类似于grep的命令行工具,但是搜索速度更快,能力比grep更强。尤其是当你是程序员时,我强烈推荐你使用ack来取代grep。

ack的用法非常适用与代码搜索,因此程序员可以在源码树中进行复杂的查询,而只需要更少的按键。

ack的一些非常强大的特性:

  • 默认搜索当前工作目录
  • 默认递归搜索子目录
  • 忽略元数据目录,比如.svn.gitCSV等目录
  • 忽略二进制文件(比如pdf,image,coredumps)和备份文件(比如foo~*.swp)
  • 在搜索结果中打印行号,有助于找到目标代码
  • 能搜索特定文件类型(比如Perl,C++,Makefile),该文件类型可以有多种文件后缀
  • 高亮搜索结果
  • 支持Perl的高级正则表达式,比grep所使用GNU正则表达式更有表现力。

相比于搜索速度,ack总体上比grep更快。ack的速度只要表现在它的内置的文件类型过滤器。在搜索过程中,ack维持着认可的文件类型的列表,同时跳过未知或不必要的文件类型。它同样避免检查多余的元数据目录。

在Linux上安装ack

尽管在大多数Linux发行版中是ack是标准包,可轻易获得(比如在基于debian的系统中,是ack-grep包,而在基于Redhat的系统中则是ack包),但是与发行版捆绑的ack版本仍然是1.x,而ack2.0已经发布,而且拥有更多特性。

因此我准备在官方网站下载,然后安装ack。

方便的是,ack在官网可可作为一个单独的Perl脚本获得,其中整合了所有需要依赖的模块。因此,你不需要额外安装Perl模块来运行这脚本。

为了在你的Linux系统中安装ack,去官网下载最新版本的ack。在写本文时,最新的版本是2.12

$ wget http://beyondgrep.com/ack-2.12-single-file
$ sudo mv ack-2.12-single-file /usr/local/bin/ack
$ sudo chmod 0755 /usr/local/bin/ack

需要注意的是,在基于Debian的系统中,有一个独立的包也叫ack(汉码转换器)。所以如果你碰巧有使用那个包,那么你就必须重命名ack来避免命名冲突了。

ack的使用案例

1.在当前目录递归搜索单词'eat',不匹配类似于'feature'或'eating'的字符串:

$ ack -w eat

2.搜索有特殊字符的字符串$path=.,所有的元字符需要在字面上被匹配:

$ ack -Q '$path=.' /etc

3.除了dowloads目录,在所有目录搜索about单词:

$ ack about --ignore-dir=downloads

4.只搜索包含protected单词的PHP文件,然后通过文件名把搜索结果整合在一起,打印每个文件对应的搜索结果:

图

5.获取包含CFLAG关键字的Makefile的文件名。文件名为*.mkmakefileMakefileGNUmakefile的都在考虑范围内:

$ ack --make -l CFLAG

6.显示整个日志文件时高亮匹配到的字符串:

$ tail -f /var/log/syslog | ack --passthru 192.168.1.10

7.要换取ack支持的文件过滤类型,运行:

$ ack --help-type