使用find命令搜索可执行文件

187

我可以使用什么类型的参数/标志与Unix find命令一起使用,以便搜索可执行文件?


我认为“-executable”是你想要的选项。 - sje397
6
find -executable... 但这并不能保证列出的每个文件都能真正执行。 - William
1
并非所有的find实现都是相同的。@sje397和@William推荐的选项可能不可用。最好使用下面显示的被接受的解决方案 - Mr. Lance E Sloan
1
我不喜欢下面所有基于文件权限的提案。理由是:对于我的GNU操作系统(Ubuntu),可以为ASCII文本文件设置“x”(可执行)标志。没有模拟器阻止了此操作的成功完成。只需要一个小错误/漏洞,就会将x标志分配给多个非预期的文件。因此,gniourf_gniourf的解决方案是我个人最喜欢的。但是它有一个缺点,即交叉编译的可执行文件需要模拟器或目标设备。 - user5494920
@William,这只能找到具有可执行权限位的文件。要实际确定某个文件是否为可执行文件,无论它是否具有或不具有任何权限,无论它是一个 shebang shell 脚本还是编译二进制文件,请尝试使用 file 命令,即 file $FILEPATH | grep -q executable,如果 $FILEPATH 看起来像一个可执行文件,则返回 true。这是一个很棒的小命令,虽然我不确定它有多常见(在 macOS 上预装)。确保你的 file 命令是最新的,以便它可以正确检测大多数 shebangs。 - tmillr
10个回答

278
在GNU版本的find命令中,您可以使用-executable参数:
find . -type f -executable -print

对于BSD版本的find命令,你可以使用-perm和一个八进制掩码+

find . -type f -perm +111 -print

在这个上下文中,“+”表示“任何这些位都被设置”,而111是执行位。
请注意,这与GNU find中的“-executable”谓词并不完全相同。特别是,“-executable”测试文件是否可以由当前用户执行,而“-perm +111”只是测试是否设置了任何执行权限。
旧版本的GNU find也支持“-perm +111”语法,但从4.5.12开始,不再支持此语法。相反,您可以使用“-perm /111”来获得此行为。

2
在findutils 4.5.11 4.fc20中出现错误“find: invalid mode ‘+111’”。 - sourcejedi
1
@sourcejedi 谢谢。我实际上只是在谈论非GNU版本的find(特别是BSD),但旧版本的GNU find也支持该语法。在较新的版本中,您将不得不使用“/”而不是“+”。有关更多详细信息,请参见更新的答案。 - Laurence Gonsalves
如果要查找符号链接到可执行文件,包括-L选项:find -L ... - mklement0
我花了一段时间才明白“不等同于-executable谓词”和“只是测试是否设置了任何执行权限”的含义:这意味着-perm +111可能会产生“误报”,即当前用户实际上无法执行的文件。仅通过测试权限无法模拟-executable,因为需要将文件的用户和组标识与当前用户的标识相对应。 - mklement0
一个与POSIX兼容的等效命令find . -type f -perm +111 -printfind . -type f \( -perm -u=x -o -perm -g=x -o -perm -o=x \) - mklement0
显示剩余6条评论

60

致敬 @gniourf_gniourf,感谢他澄清了一个基本误解。

本回答试图提供现有回答的概述,并讨论它们的微妙之处和相对优点,以及提供背景信息,特别是关于可移植性方面。

查找可执行文件可以涉及两个不同的用例

  • 用户为中心:查找当前用户可以执行的文件
  • 文件为中心:查找具有(一个或多个)可执行权限位设置的文件。

请注意,在任何一个场景中,使用find -L ...而不仅仅是find ...可能是有意义的,以便还可以查找到指向可执行文件的符号链接

请注意,最简单的基于文件的情况——查找可执行权限位设置为所有三个安全主体(用户、组、其他)的可执行文件——通常会产生与以用户为中心的场景相同的结果,但并不一定如此,了解差异很重要。
用户中心(-executable
  • The accepted answer commendably recommends -executable, IF GNU find is available.

    • GNU find comes with most Linux distros
      • By contrast, BSD-based platforms, including macOS, come with BSD find, which is less powerful.
    • As the scenario demands, -executable matches only files the current user can execute (there are edge cases.[1]).
  • The BSD find alternative offered by the accepted answer (-perm +111) answers a different, file-centric question (as the answer itself states).

    • Using just -perm to answer the user-centric question is impossible, because what is needed is to relate the file's user and group identity to the current user's, whereas -perm can only test the file's permissions.
      Using only POSIX find features, the question cannot be answered without involving external utilities.
    • Thus, the best -perm can do (by itself) is an approximation of -executable. Perhaps a closer approximation than -perm +111 is -perm -111, so as to find files that have the executable bit set for ALL security principals (user, group, other) - this strikes me as the typical real-world scenario. As a bonus, it also happens to be POSIX-compliant (use find -L to include symlinks, see farther below for an explanation):

      find . -type f -perm -111  # or: find . -type f -perm -a=x
      
  • gniourf_gniourf's answer provides a true, portable equivalent of -executable, using -exec test -x {} \;, albeit at the expense of performance.

    • Combining -exec test -x {} \; with -perm +111 (i.e., files with at least one executable bit set) may help performance in that exec needn't be invoked for every file (the following uses the POSIX-compliant equivalent of BSD find -perm +111 / GNU find -perm /111; see farther below for an explanation):

      find . -type f \( -perm -u=x -o -perm -g=x -o -perm -o=x \) -exec test -x {} \; -print
      

以文件为中心 (-perm)

为了回答与文件相关的问题,使用符合POSIX标准的-perm主命令就足够了(在GNU find术语中称为“测试”)。
  • -perm允许您测试任何文件权限,而不仅仅是可执行性。
  • 权限可以用八进制或符号模式指定。八进制模式是八进制数(例如111),而符号模式是字符串(例如a=x)。
  • 符号模式将安全主体标识为u(用户)、g(组)和o(其他),或者a表示所有三个。权限用=+-运算符分配给主体;有关完整讨论,包括八进制模式,请参见chmod实用程序的POSIX规范
  • find的上下文中:
    • 在模式前加上-(例如-ug=x)表示:匹配具有全部指定权限的文件(但匹配的文件可能具有其他权限)。
    • 没有前缀(例如755)表示:匹配具有这个完整、精确权限集的文件。
    • 注意:GNU find和BSD find都实现了一个额外的、非标准的前缀,用于任何指定权限位是否设置的逻辑,但它们使用不兼容的语法
      • BSD find:+
      • GNU find:/ [2]
    • 因此,如果您的代码必须是可移植的,请避免使用这些扩展功能
以下示例演示了对各种与文件相关的问题的可移植答案。

以文件为中心的命令示例

注意:

The following examples are POSIX-compliant, meaning they should work in any POSIX-compatible implementation, including GNU find and BSD find; specifically, this requires:
- NOT using nonstandard mode prefixes + or /. - Using the POSIX forms of the logical-operator primaries: - ! for NOT (GNU find and BSD find also allow -not); note that \! is used in the examples so as to protect ! from shell history expansions - -a for AND (GNU find and BSD find also allow -and) - -o for OR (GNU find and BSD find also allow -or)
The examples use symbolic modes, because they're easier to read and remember.
- With mode prefix -, the = and + operators can be used interchangeably (e.g., -u=x is equivalent to -u+x - unless you apply -x later, but there's no point in doing that). - Use , to join partial modes; AND logic is implied; e.g., -u=x,g=x means that both the user and the group executable bit must be set. - Modes cannot themselves express negative matching in the sense of "match only if this bit is NOT set"; you must use a separate -perm expression with the NOT primary, !.
Note that find's primaries (such as -print, or -perm; also known as actions and tests in GNU find) are implicitly joined with -a (logical AND), and that -o and possibly parentheses (escaped as \( and \) for the shell) are needed to implement OR logic.
find -L ... instead of just find ... is used in order to also match symlinks to executables
- -L instructs find to evaluate the targets of symlinks instead of the symlinks themselves; therefore, without -L, -type f would ignore symlinks altogether.
# Match files that have ALL executable bits set - for ALL 3 security
# principals (u (user), g (group), o (others)) and are therefore executable
# by *anyone*.
# This is the typical case, and applies to executables in _system_ locations
# (e.g., /bin) and user-installed executables in _shared_ locations
# (e.g., /usr/local/bin), for instance. 
find -L . -type f -perm -a=x  # -a=x is the same as -ugo=x

# The POSIX-compliant equivalent of `-perm +111` from the accepted answer:
# Match files that have ANY executable bit set.
# Note the need to group the permission tests using parentheses.
find -L . -type f \( -perm -u=x -o -perm -g=x -o -perm -o=x \)

# A somewhat contrived example to demonstrate the use of a multi-principial
# mode (comma-separated clauses) and negation:
# Match files that have _both_ the user and group executable bit set, while
# also _not_ having the other executable bit set.
find -L . -type f -perm -u=x,g=x  \! -perm -o=x

[1] GNU find 4.4.2 中 -executable 的描述:

匹配可执行文件和可搜索的目录(在文件名解析意义上)。这将考虑访问控制列表和其他权限工件,而 -perm 测试会忽略它们。此测试使用 access(2) 系统调用,因此可能会被 NFS 服务器欺骗,因为许多系统在客户端内核中实现 access(2),无法利用服务器上保存的 UID 映射信息。由于此测试仅基于 access(2) 系统调用的结果,因此不能保证此测试成功的文件实际上可以被执行。

[2] GNU find版本早于4.5.12也允许前缀+,但是这个功能已经被弃用并最终删除,因为将+符号模式组合可能会产生意外的结果,因为它被解释为一个精确的权限掩码。如果您(a)运行在之前4.5.12版本,并且(b)只限于使用八进制模式,那么您可以在GNU find和BSD find中同时使用+,但这不是个好主意。


7
史上最全面的SO回答? ;) - andynormancx
1
@andynormancx :) 嗯,就纯条目数量而言,我可以提供这个竞争者 - mklement0

13

为了有另一种可能性1找到当前用户可执行的文件:

find . -type f -exec test -x {} \; -print

这里使用的测试命令是在PATH中找到的那个,很可能是 /usr/bin/test,而不是内建的。


1 仅在 find-executable 标志不可用时使用!这与 -perm +111 解决方案略有不同。


2
这个可以用,但是速度比较慢。而且,根据不同的 shell,你可能需要包装或转义文件名占位符,例如 '{}' 或者 \{\} - Ionoclast Brigham
1
谢谢,@gniourf_gniourf - 我确实有一些误解。我在这里重新印刷你的另一个评论,因为我现在至少删除了我的答案(也许可以复活,如果有可挽救的东西):“find . -type f -perm -u=x不是-executable的等价物:-executable匹配用户可以执行的所有文件,包括g+x(如果我在适当的组中)或o+x。实际上,-perm -u=x会找到许多用户无法执行的文件,并错过一些用户可以执行的文件。” - mklement0
1
@IonoclastBrigham:虽然引用 {} 是一种假设性的必要性(并且引用不会有害),但在实践中,在类 POSIX 的 shell 和 csh 中并不需要。你知道哪些需要这样做的 shell 吗? - mklement0
1
@mklement0 是的,这就是我相信的 :) - gniourf_gniourf
4
@IonoclastBrigham: 有趣,谢谢;因此,在 fish 中,{} 必须被转义为 '{}'\{\}。注意,bashkshzsh 都提供了同样类型的花括号扩展;然而,它们会原样打印未引用的标记 {}(因此不需要转义),因为它们不认为这是一个有效的花括号表达式(它们至少需要两个标记或有效的数字序列表达式),而 fish 则将 {} 视为产生空字符串的有效花括号表达式。 - mklement0
显示剩余8条评论

12

你可以使用-executable测试标志:

-executable
              Matches files which are executable  and  directories  which  are
              searchable  (in  a file name resolution sense).

5
"-executable" 被认为是一个未知选项。 - well actually
6
那是否是GNU Find的扩展?由于标签是Unix而不是Linux,因此至少需要将GNU扩展说明为这样。 - Jonathan Leffler
3
BSD find命令不支持此选项,至少在OS X上是如此。这是GNU的扩展功能,但其他版本的find命令可能会支持。 - Ionoclast Brigham
就我所知,这在SLES 10上不适用,但在SLES >= 11上适用(我因此遭遇了一点困难)。 - Peter Turner
请注意,这实际上并没有获取所有的示例。在我的情况下,我拥有一个文件,其权限为“-rw-r-xr-x”,而“-executable”无法检测到它。 - Dezza
@Dezza:如果该文件是由您拥有或您是该文件组的成员,则可能会发生这种情况。在这种情况下,该文件对您来说是不可执行的,因此“-executable”不会报告它。 - mklement0

5
find . -executable -type f

这并不真正保证文件可执行,它只是查找具有执行位设置的文件。如果您想要

chmod a+x image.jpg

上面的`find`命令会认为`image.jpg`是一个可执行文件,即使它实际上只是一张带有执行权限的JPEG图像。

通常我会通过以下方式解决这个问题:

find . -type f -executable -exec file {} \; | grep -wE "executable|shared object|ELF|script|a\.out|ASCII text"

如果您希望查找实际上打印可执行文件的一些信息,可以按照以下方式操作:
find . -type f -executable -printf "%i.%D %s %m %U %G %C@ %p" 2>/dev/null |while read LINE
do
  NAME=$(awk '{print $NF}' <<< $LINE)
  file -b $NAME |grep -qEw "executable|shared object|ELF|script|a\.out|ASCII text" && echo $LINE
done

在上面的例子中,文件的完整路径名在最后一个字段中,必须反映您在awk "NAME=$(awk '{print $NF}' <<< $LINE)"中查找它的位置。如果文件名在find输出字符串的其他位置,则需要将“NF”替换为正确的数字位置。如果您的分隔符不是空格,则还需要告诉awk您的分隔符是什么。

3

这对我有用,想要分享...

find ./ -type f -name "*" -not -name "*.o" -exec sh -c '
    case "$(head -n 1 "$1")" in
      ?ELF*) exit 0;;
      MZ*) exit 0;;
      #!*/ocamlrun*)exit0;;
    esac
exit 1
' sh {} \; -print

16
只需要再增加几千个案例,你就会重新发明“文件”! - tripleee
@tripleee +1。很酷的扩展是:find ./ -mime application/x-sharedlib -o -mime application/x-dosexec - Daniel Alder
@Daniel Alder,你使用的是哪个版本的find命令?我在find (GNU findutils) 4.4.2中没有找到-mime选项。 - AjayKumarBasuthkar
@tripleee +1。利用“file”和/或“mimetype”是个好主意,但发现支持-mime的版本会更好。另外,我想知道“file”/“mimetype”是否有过滤并仅显示可执行文件的选项。 - AjayKumarBasuthkar

2

如果您想查找可执行文件类型(例如脚本、ELF二进制文件等等),而不仅仅是具有执行权限的文件,那么您可能需要执行类似以下命令的操作(其中当前目录“.”可以替换为您想要的任何目录):

find . -type f -executable

这将返回在指定目录中所有可执行文件的列表。

 gfind . -type f -exec bash -c '[[ $(file -b "'{}'") == *" executable "* ]] ' \; -print

如果您没有使用macports(Linux用户)或以其他方式安装了GNU find,您需要使用以下命令:

最初的回答:

 find . -type f -exec bash -c '[[ $(file -b "'{}'") == *" executable "* ]] ' \; -print

虽然如果您在OS X上,它会自带一个名为is_exec的小实用程序,该实用程序基本上会将那个小测试打包起来,以便您可以缩短命令行,但是这种方式更加灵活,因为您可以轻松地将== 测试替换为=~测试,并用它来检查更复杂的属性,例如可执行纯文本文件或其他文件命令返回的信息。
这里的引言确切规则非常模糊,所以我最终只能通过试验来解决问题,但我很想听到正确的解释。

使用 find . -type f -execdir sh -c "file -b {} | grep -q executable" \; -execdir realpath {} + 命令可以让你改变 realpath,以便对可执行文件进行操作。请注意,这不适用于 可能 是可执行文件的文件,例如 .sh 空文件。 - Pablo Bianchi

1

我遇到了同样的问题,答案在 dmenu源代码 中:为此而制作的stest实用程序。您可以编译'stest.c'和'arg.h'文件,然后它应该可以工作。有一个用法的man页面,我把它放在那里方便查看:

STEST(1)         General Commands Manual         STEST(1)

NAME
       stest - filter a list of files by properties

SYNOPSIS
       stest  [-abcdefghlpqrsuwx]  [-n  file]  [-o  file]
       [file...]

DESCRIPTION
       stest takes a list of files  and  filters  by  the
       files'  properties,  analogous  to test(1).  Files
       which pass all tests are printed to stdout. If  no
       files are given, stest reads files from stdin.

OPTIONS
       -a     Test hidden files.

       -b     Test that files are block specials.

       -c     Test that files are character specials.

       -d     Test that files are directories.

       -e     Test that files exist.

       -f     Test that files are regular files.

       -g     Test  that  files  have  their set-group-ID
              flag set.

       -h     Test that files are symbolic links.

       -l     Test the contents of a directory  given  as
              an argument.

       -n file
              Test that files are newer than file.

       -o file
              Test that files are older than file.

       -p     Test that files are named pipes.

       -q     No  files are printed, only the exit status
              is returned.

       -r     Test that files are readable.

       -s     Test that files are not empty.

       -u     Test that files have their set-user-ID flag
              set.

       -v     Invert  the  sense  of  tests, only failing
              files pass.

       -w     Test that files are writable.

       -x     Test that files are executable.

EXIT STATUS
       0      At least one file passed all tests.

       1      No files passed all tests.

       2      An error occurred.

SEE ALSO
       dmenu(1), test(1)

                        dmenu-4.6                STEST(1)

1
很简单的回答是:“你的可执行文件在包含在你的PATH变量中的目录中”,但这并不能真正找到你的可执行文件,而且可能会错过很多可执行文件。我不太了解Mac,但我认为“mdfind 'kMDItemContentType=public.unix-executable'”可能会错过像解释脚本这样的东西。
如果你可以通过查找设置了可执行位的文件(无论它们是否实际上可执行)来找到文件,则可以这样做。
find . -type f -perm +111 -print

如果支持,"-executable"选项将进一步过滤查看acl和其他权限工件,但在技术上与"-pemr +111"没有太大区别。

也许将来find会支持"-magic",并允许您明确查找具有特定魔术ID的文件...但那时您将不得不指定要查找所有可执行格式的魔术ID。

我不知道在Unix上是否有一个技术上正确且简单的解决方法。


1
这太荒谬了,这不应该如此困难,更不用说几乎是不可能的。举起手来,我向Apple/Spotlight投降... mdfind 'kMDItemContentType=public.unix-executable' 至少这个方法有效!

了解在OSX上的mdfind是很好的。请注意,您的命令将报告整个系统的Unix可执行文件。mdfind -onlyin . 'kMDItemContentType=public.unix-executable' 将结果限制为当前目录的子树。有趣的细节:似乎不支持将搜索限制为仅特定目录(不包括子文件夹)。符号链接到可执行文件显然永远不会被包含。奇怪的是,一旦mdfind已经发现文件是可执行的,后来_删除_可执行位不会被检测到。 - mklement0
我认为我已经发现了Spotlight如何检测/未检测可执行Unix文件的漏洞;我已向苹果公司和http://www.openradar.me/20162683提交了一个漏洞报告。我鼓励你和其他对此功能感兴趣的人也在https://bugreport.apple.com上提交漏洞报告。 - mklement0
mdfind -onlyin . 'kMDItemContentType=public.unix-executable' 的行为类似于 find . -type f -perm +111 -print。也就是说,它会找到任何可执行位设置的文件,这可能会产生误报(尽管在实践中可能不是问题)- 要想真正只查找当前用户可执行的文件,请参见 @gniourf_gniourf 的答案。使用基于 find 的解决方案的优点是,如果需要(选项 -L),您可以找到指向可执行文件的符号链接,而 mdfind 似乎无法做到这一点。 - mklement0
1
@mklement0 我的回答避免了华丽修饰 - 为了强调重点 - 但是是的,你几乎永远不会使用这种形式"ungarnished"。另一个选择 - 不确定是否提到过 - 是 "好老的 globbing".. ls /Applications/**/*(*) 在你(我的?)zsh shell 中。 - Alex Gray
感谢您提供有用的zsh提示 - 我之前不知道; (似乎您可以匹配可执行文件(*)或符号链接(@),但不能同时匹配,对吗?)。至于您最初的观点:让我重申:find . -type f -perm +a=x将执行您的mdfind命令所做的操作,同时提供更多的灵活性。您甚至可以重新制定它以符合POSIX标准。 - mklement0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接