`find -exec`里面有什么奇怪的语法?

时间:2017-08-10 10:58:35

标签: bash find exec

最近我遇到了一个奇怪的bash脚本,用于从find -exec内部调用自定义bash函数。我开发了以下简单的脚本来演示我需要解释的功能。

在以下示例中,将为每个foo结果调用函数find

foo()                                                                                                                                                 
{
    echo "$@"
}
export -f foo

find . -exec bash -c 'foo "$@"' bash {} \;

有人可以解释-exec之后的部分是如何解释的吗?

更新

为了进一步简化这一点,在导出foo之后,对每个find结果执行以下操作(假设有一个名为my_file的文件)。

bash -c 'foo "$#"' bash my_file

这会产生输出myfile。我不明白这是如何工作的。那第二个bash有什么作用?任何详细的解释表示赞赏。

(请注意,此问题与find命令无关。另请注意函数foo的功能,我只想导出一些函数)

1 个答案:

答案 0 :(得分:3)

要了解你需要知道4件事:

  1. find操作-exec允许您对找到的文件和目录应用命令。

  2. -c bash选项记录如下:

    BASH(1)
    ...
    OPTIONS
    ...
           -c        If the -c option is present, then commands are read from
                     the first non-option  argument  command_string.
                     If  there are arguments after the command_string, they
                     are assigned to the positional parameters, starting with $0.
    
    ...
    
    If bash is started with the -c option, then $0 is set to the first
    argument after the string to be executed, if one is present.
    Otherwise, it is set to the filename used to invoke bash, as given
    by argument zero.
    
  3. bash中,$@展开所有位置参数($1$2 ...),从参数$1开始。

  4. bash函数中,位置参数是调用函数时传递给函数的参数。

  5. 因此,在您的情况下,为每个找到的文件或目录执行的命令是:

    bash -c 'foo "$@"' bash <the-file>
    

    因此,位置参数设置为:

    $0 = bash
    $1 = <the-file>
    
    要求

    bash在此上下文中执行'foo "$@"'"$@"首先展开为"<the-file>"。因此,使用一个参数调用函数foo"<the-file>"。在函数foo的上下文中,位置参数因此是:

    $1 = "<the-file>"
    

    echo "$@"展开为echo "<the-file>"

    所有这些只打印所有找到的文件或目录的名称。这几乎就像你有任何一样:

    find . -exec echo {} \;
    find . -print
    find .
    find
    

    (对于接受最后一个版本的find个版本。)

    几乎就像一样,因为如果文件或目录名称包含空格,取决于您对find和引号的使用,您将得到不同的结果。因此,如果您打算使用更复杂的foo函数,则应注意引号。例子:

    $ touch "filename with spaces" plain
    $ ls -1
    filename with spaces
    plain                                                # 2 files
    $ foo() { echo "$@"; }                               # print arguments
    $ find . -type f
    ./filename with spaces
    ./plain
    $ find . -type f -exec bash -c 'foo "$@"' bash {} \;
    ./filename with spaces
    ./plain
    $ find . -type f -exec bash -c 'foo $@' bash {} \;
    ./filename with spaces
    ./plain
    

    3 find命令显然是这样做的,但是:

    $ bar() { echo $#; }                                 # print number of arguments
    $ wc -w < <(find . -type f)
    4                                                    # 4 words
    $ find . -type f -exec bash -c 'bar "$@"' bash {} \;
    1                                                    # 1 argument
    1                                                    # per file
    $ find . -type f -exec bash -c 'bar $@' bash {} \;
    3                                                    # 3 arguments
    1                                                    # 1 argument
    

    使用find . -type f -exec bash -c 'bar "$@"' bash {} \;,第一个文件名作为一个参数传递给函数bar,而在所有其他情况下,它被视为3个单独的参数。