路径斜杠后的 Bash 大括号扩展

路径斜杠后的 Bash 大括号扩展

我正在尝试使用大括号扩展将文件以不同的名称复制到同一目录中。我正在使用 bash 4.4.18。

这就是我所做的:

cp ~/some/dir/{my-file-to-rename.bin, new-name-of-file.bin}

但我收到此错误:

cp: cannot stat '/home/xyz/some/dir/{my-file-to-rename.bin,': No such file or directory

即使像这样简单的大括号扩展也会给我带来同样的错误:

cp {my-file-to-rename.bin, new-name-of-file.bin}

我究竟做错了什么?

答案1

大括号扩展语法接受逗号,但不接受逗号后的空格。在许多编程语言中,逗号后面的空格很常见,但这里不是。在 Bash 中,未加引号的空格的存在会阻止执行大括号扩展。

去掉空格,就可以了:

cp ~/some/dir/{my-file-to-rename.bin,new-name-of-file.bin}

虽然完全不需要,但请注意,您可以将尾随移动.bin到大括号之外:

cp ~/some/dir/{my-file-to-rename,new-name-of-file}.bin

如果想测试大括号展开的效果,可以使用echoor printf '%s ', orprintf使用您喜欢的任何格式字符串来执行此操作。 (就我个人而言echo,当我在 Bash 中时,我只是用这个,因为Bash 的echo内置默认情况下不会扩展转义序列,因此非常适合检查实际运行的命令。)例如:

ek@Io:~$ echo cp ~/some/dir/{my-file-to-rename,new-name-of-file}.bin
cp /home/ek/some/dir/my-file-to-rename.bin /home/ek/some/dir/new-name-of-file.bin

答案2

string{foo, bar}不是大括号扩展;就是string{foo,和这两个词bar}。要使用大括号扩展,大括号需要位于同一个单词内。如果不需要,则必须删除多余的空格;如果确实需要,则必须引用它:

$ printf "%s\n" aa{bb, cc}
aa{bb,
cc}
$ printf "%s\n" aa{bb,cc}
aabb
aacc
$ printf "%s\n" aa{bb," cc"}
aabb
aa cc

答案3

Bash 对待这个空间就像对待其他空间一样。作为 IFS,内部字段分隔符。这用于扩展后的字分割,并使用 read 内置命令将行分割成字。

shell 将 IFS 的每个字符视为分隔符,并将其他扩展的结果拆分为这些字符上的单词。如果 IFS 未设置,或其值恰好为默认值,则忽略先前扩展结果的开头和结尾处的 、 、 和 序列,并且不在开头或结尾处的任何 IFS 字符序列用于分隔字。如果 IFS 具有默认值以外的值,则只要空白字符位于 IFS 值(IFS 空白字符)中,单词开头和结尾的空白字符 space 和 tab 序列就会被忽略。 IFS 中非 IFS 空白的任何字符以及任何相邻的 IFS 空白字符共同界定字段。 IFS 空白字符序列也被视为分隔符。如果IFS的值为空,则不会发生分词。
-bash(1)

通过插入未转义的分隔符,然后您告诉 bash 您的命令和参数是:

  1. “cp”
  2. “~/some/dir/{要重命名的文件.bin”
  3. “新文件名.bin}”

如果你有引号或转义符“\”,你会:

  1. “cp”
  2. “~/some/dir/{要重命名的文件.bin,\ 新文件名.bin}”

这也不是你想要的,除非“ new-name-of-file.bin”是你想要的新文件名。包括空格。由于首先发生括号扩展,然后发生波浪符号扩展,因此 bash 将执行:

  1. “cp”
  2. “/path/to/home/some/dir/my-file-to-rename.bin”
  3. “/path/to/home/some/dir/\ 新文件名.bin”

只需删除空间即可解决所有问题。

相关内容