为什么 $() 不能正确扩展?

为什么 $() 不能正确扩展?

我想使用 vlc 播放目录中的所有 .mp3 文件,所以我这样做:

vlc $(find ~/Documents/music -name "*.mp3" -exec "echo \"{}\"" \;)

问题是我得到“vlc:未知选项或缺少强制参数‘-D’”。估计是解析问题。

因此,让我们逐个文件进行操作:

echo $(echo $(find ~/Documents/music -name "*.mp3" -exec echo "\"{}\" \n " \;) | head -n 1)

我得到:

"/home/XXX/Documents/music/My Folder/1 - track/the name of track.mp3"

当我跑步时:

vlc "/home/XXX/Documents/music/My Folder/1 - track/the name of track.mp3"

有用。

但当我跑步时

vlc $(echo $(find ~/Documents/music -name "*.mp3" -exec echo "\"{}\" \n " \;) | head -n 1)

我得到:

cannot open file /home/XXX/the (No such file or directory)

如果 vlc 的参数没有被正确引用,这将是有意义的。但他们是吗?那么为什么它会作为不带引号的字符串传递到 vlc 中呢?

答案1

扩展的输出中不会处理引号。如果你这样做$(echo '"foo bar"'),或$(echo "\"foo bar\""),最终作为主命令的参数是"fooand bar"。使用字面引号,并作为两个不同的参数,因为分词

默认情况下,分词发生在任何空格、空格或换行符上,因此从 的输出中find,您无法区分哪个空格是文件名的一部分,以及哪个空格是find用于分隔它们的打印内容的一部分。即使在一般情况下,您也无法区分它们,因为文件名也可以包含换行符......

你能做的是:

find ... -print0 | xargs -0 vlc 

或者

find ... -exec vlc {} +

第一个find输出用 NUL 字节分隔的文件名,并告诉xargs我们期待这一点。但并非所有系统都有find -print0xargs -0

第二个函数find本身已启动vlc,尾部{} +告诉它将多个文件名传递给vlc.

如果您要查找大量文件,并且它们不适合在一个命令行中显示,那么这两个文件都可能会启动vlc多次。

看:

使用echo它来调试并不能真正起作用,因为它将用一个空格连接其参数,从而无法区分echo foo bar, 和echo "foo bar"

答案2

扩展$()正确,但 shell 对结果的作用比您意识到的要多。此外,向路径名添加文字引号几乎从来都不是正确的做法(除非您eval在某个地方使用)。

要播放 中或下的所有 MP3 文件,~/Documents/music请使用vlc

find ~/Documents/music -type f -name '*.mp3' -exec vlc {} +

这将vlc一次运行尽可能多的 MP3 文件。

要跳过使用findall-together:

shopt -s globstar
vlc ~/Documents/music/**/*.mp3

只要您没有大量 MP3 文件,这种方法就可以工作(在这种情况下,您会收到错误,因为参数列表变得太长)。

glob**会“递归地”匹配文件层次结构,但必须使用shopt -s globstarin启用bashzshshell 默认情况下启用此 glob)。


你的命令

vlc $(echo $(find ~/Documents/music -name "*.mp3" -exec echo "\"{}\" \n " \;) | head -n 1)

首先执行find,headecho.该管道将​​输出

"/home/XXX/Documents/music/My Folder/1 - track/the name of track.mp3"

然后 shell 会将该字符串拆分为单词"/home/XXX/Documents/music/My, Folder/1, -, track/the, name, of, 以及track.mp3"字符串中的空格。

vlc然后将被称为那些分离论点。特别是,该-参数可能会被误认为是一个选项,而其他参数很可能被视为单独的路径名操作数。

这在中进行了深入解释

相关内容