为什么我不需要通过" \" {} \""处理带空格的文件名时的xargs?

时间:2017-11-12 15:08:53

标签: shell find xargs

我有以下问题:

我想使用xargs-find构造来查找文件。 区别在于,我不想在命令中使用find作为命令1,而是使用命令2:

command1 | xargs command2

如果文件名名称中包含空格,则会出现问题

例如:

如果我正在尝试:

echo 01.Here Comes The Night Time II.flac | xargs -pi find ~/Multimedia/Musik/flac/ -name "\""{}"\""

find ~/Multimedia/Musik/flac/ -name "01.Here Comes The Night Time II.flac" ?...yes

什么都没找到。 使用xargs的-0选项也无法正常工作。

如果我从xargs复制并粘贴交互式打印请求,则会找到该文件:

find ~/Multimedia/Musik/flac/ -name "01.Here Comes The Night Time II.flac"

~/Multimedia/Musik/flac/Arcade Fire/Reflektor (CD 2)/01.Here Comes The Night Time II.flac

我和#34;饲料"的方式是否有问题?管道,或我包括"在find命令中(我通过反复试验得出)或其他什么?

1 个答案:

答案 0 :(得分:2)

您似乎对程序在内部启动的方式以及shell如何解释命令感到困惑。

在unix中,启动程序涉及三个参数:

  1. 文件名。这是一个包含要运行的程序路径的字符串。
  2. 字符串列表。按照惯例,我们称这些"命令行参数"。
  3. 另一个字符串列表。按照惯例,我们将这些称为环境",但在操作系统级别,它只是另一个字符串列表。但是,所有程序/库都会密切关注用户和应用程序员的隐藏。
  4. 当您在shell中键入命令时,会发生很多事情,但在最简单的情况下,它只是一堆以空格分隔的单词:

    $ foo bar baz
    

    $表示shell提示符,而不是您键入的内容。)

    shell将此行拆分为三个单词(foobarbaz)并将第一行解释为程序名称(在列出的目录中查找)在PATH变量中。我们假设PATH列出/usr/bin,确实有一个/usr/bin/foo计划。

    现在shell启动程序如下(伪代码):

    exec("/usr/bin/foo", ["foo", "bar", "baz"], [...])
    

    即。我们在/usr/bin/foo中运行可执行文件,传递三个字符串的列表作为参数。 ([...]代表环境,从现在开始我们将忽略这一环境。)

    如果你这样做会怎么样?

    $ foo "bar baz"
    

    引号会影响shell将行拆分为单词的方式。特别是,引号中的" "(空格)不作为分隔符,而是从字面上理解。这为我们提供了一个双元素列表(foobar baz)。请注意,引号不是单词本身的一部分。

    在内部,这转换为以下调用:

    exec("/usr/bin/foo", ["foo", "bar baz"], [...])
    

    同样,第二个参数只包含一个空格。没有嵌入式引号。

    那么像

    这样的命令会发生什么
    $ xargs -pi find ~/Multimedia/Musik/flac/ -name "\""{}"\""
    

    这将再次被shell解析为单词列表。 ~将替换为您的主目录的名称。 "\""只是一种写作\"'"'(即文字"字符)的复杂方式。我们最终得到的列表是xargs-pifind/home/madZeo/Multimedia/Musik/flac/-name"{}"。这转换为以下调用:

    exec("/usr/bin/xargs", ["xargs", "-pi", "find", "/home/madZeo/Multimedia/Musik/flac/", "-name", "\"{}\""], [...])
    

    注意最后一个参数是4个字符的字符串"{}"

    xargs将其第一个参数(-pi)视为选项规范。特别是,-i告诉它用标准输入中读取的当前值替换参数列表中的{}

    xargs然后从其标准输入中读取一行,(由于您的echo管道)提供了01.Here Comes The Night Time II.flac

    代替{}取代find/home/madZeo/Multimedia/Musik/flac/-name"01.Here Comes The Night Time II.flac"xargs然后像这样调用find

    exec("/usr/bin/find", ["find", "/home/madZeo/Multimedia/Musik/flac/", "-name", "\"01.Here Comes The Night Time II.flac\""], [...])
    

    这告诉find查找名称字面上以"(引号字符)开头的文件。没有这样的文件,因此失败。

    修复方法是编写如下命令:

    $ xargs -pi find ~/Multimedia/Musik/flac/ -name {}
    

    这最终会运行

    exec("/usr/bin/find", ["find", "/home/madZeo/Multimedia/Musik/flac/", "-name", "01.Here Comes The Night Time II.flac"], [...])
    

    ,这就是你想要的。

    问题是xargs直接运行其子命令(在这种情况下为find)。它不构造一个由shell重新解析的新命令行。它没有在空格上分割它的传入参数,它不解释引号,它不关心"特殊的" $*\等字符。它只是获取给定的单词列表,用当前输入替换子串{}的任何出现,然后执行它。

    如果你天真地接受这个最终命令并将其粘贴到你的shell中,它将进行单词拆分,引用删除等,从而导致不同的结果。