为什么在处理带空格的文件名时不需要将“\”{}\“”传递给 xargs?
Why don't I need to pass "\"{}\"" to xargs when handling filenames with spaces?
我有以下问题:
我想使用 xargs-find 结构来查找文件。
区别在于,我不想将 find 用作 command1,而是用作 command2 in:
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
我 "feed" 管道的方式有问题,或者我在 find 命令中包含 " 的方式有问题(我通过反复试验发现),还是其他什么?
您似乎对程序在内部启动的方式以及 shell 如何解释命令感到困惑。
在unix中,启动一个程序涉及三个参数:
- 一个文件名。这是一个包含程序路径的字符串 运行.
- 字符串列表。按照惯例,我们称这些为 "command line arguments".
- 另一个字符串列表。按照惯例,我们称这些为 "the environment",但在 OS 级别,它只是另一个字符串列表。然而,所有 programs/libraries 共同对用户和应用程序程序员隐藏此信息。
当您在 shell 中键入命令时,会发生很多事情,但在最简单的情况下,它只是一堆 space 分隔的单词:
$ foo bar baz
($
表示 shell 提示符,而不是您键入的内容。)
shell将这一行拆分为三个词(foo
、bar
、baz
)并将第一个解释为程序名(将在 PATH
变量中列出的目录中查找)。让我们假设 PATH
列出 /usr/bin
并且确实有一个 /usr/bin/foo
程序。
现在 shell 启动程序如下(伪代码):
exec("/usr/bin/foo", ["foo", "bar", "baz"], [...])
即我们 运行 /usr/bin/foo
中的可执行文件,将三个字符串的列表作为参数传递。
([...]
代表环境,从现在开始我们将忽略它。)
如果改为这样做会怎样?
$ foo "bar baz"
引号会影响 shell 将行拆分为单词的方式。特别是,引号中的 " "
(a space) 不作为分隔符,而是按字面意思使用。这给了我们一个二元素列表 (foo
, bar baz
)。请注意,引号不是单词本身的一部分。
这在内部转换为以下调用:
exec("/usr/bin/foo", ["foo", "bar baz"], [...])
同样,第二个参数只包含一个 space。没有嵌入引号。
那么像
这样的命令会发生什么
$ xargs -pi find ~/Multimedia/Musik/flac/ -name "\""{}"\""
?
这将再次被 shell 解析为单词列表。 ~
替换为您的主目录的名称。 "\""
只是 \"
或 '"'
的一种复杂的书写方式(即文字 "
字符)。我们最终得到的列表是 xargs
、-pi
、find
、/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 重新解析。它不会在 space 上拆分其传入参数,它不会解释引号,它不关心 "special" 个字符,如 $
或 *
或 \
.它只是获取给定的单词列表,用当前输入替换任何出现的子字符串 {}
,然后执行它。
如果您天真地接受这个最终命令并将其粘贴到您的 shell 中,它将进行分词、删除引号等操作,从而导致不同的结果。
我有以下问题:
我想使用 xargs-find 结构来查找文件。 区别在于,我不想将 find 用作 command1,而是用作 command2 in:
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
我 "feed" 管道的方式有问题,或者我在 find 命令中包含 " 的方式有问题(我通过反复试验发现),还是其他什么?
您似乎对程序在内部启动的方式以及 shell 如何解释命令感到困惑。
在unix中,启动一个程序涉及三个参数:
- 一个文件名。这是一个包含程序路径的字符串 运行.
- 字符串列表。按照惯例,我们称这些为 "command line arguments".
- 另一个字符串列表。按照惯例,我们称这些为 "the environment",但在 OS 级别,它只是另一个字符串列表。然而,所有 programs/libraries 共同对用户和应用程序程序员隐藏此信息。
当您在 shell 中键入命令时,会发生很多事情,但在最简单的情况下,它只是一堆 space 分隔的单词:
$ foo bar baz
($
表示 shell 提示符,而不是您键入的内容。)
shell将这一行拆分为三个词(foo
、bar
、baz
)并将第一个解释为程序名(将在 PATH
变量中列出的目录中查找)。让我们假设 PATH
列出 /usr/bin
并且确实有一个 /usr/bin/foo
程序。
现在 shell 启动程序如下(伪代码):
exec("/usr/bin/foo", ["foo", "bar", "baz"], [...])
即我们 运行 /usr/bin/foo
中的可执行文件,将三个字符串的列表作为参数传递。
([...]
代表环境,从现在开始我们将忽略它。)
如果改为这样做会怎样?
$ foo "bar baz"
引号会影响 shell 将行拆分为单词的方式。特别是,引号中的 " "
(a space) 不作为分隔符,而是按字面意思使用。这给了我们一个二元素列表 (foo
, bar baz
)。请注意,引号不是单词本身的一部分。
这在内部转换为以下调用:
exec("/usr/bin/foo", ["foo", "bar baz"], [...])
同样,第二个参数只包含一个 space。没有嵌入引号。
那么像
这样的命令会发生什么$ xargs -pi find ~/Multimedia/Musik/flac/ -name "\""{}"\""
?
这将再次被 shell 解析为单词列表。 ~
替换为您的主目录的名称。 "\""
只是 \"
或 '"'
的一种复杂的书写方式(即文字 "
字符)。我们最终得到的列表是 xargs
、-pi
、find
、/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 重新解析。它不会在 space 上拆分其传入参数,它不会解释引号,它不关心 "special" 个字符,如 $
或 *
或 \
.它只是获取给定的单词列表,用当前输入替换任何出现的子字符串 {}
,然后执行它。
如果您天真地接受这个最终命令并将其粘贴到您的 shell 中,它将进行分词、删除引号等操作,从而导致不同的结果。