我正在尝试使用大括号扩展将文件以不同的名称复制到同一目录中。我正在使用 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
如果想测试大括号展开的效果,可以使用echo
or 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 您的命令和参数是:
- “cp”
- “~/some/dir/{要重命名的文件.bin”
- “新文件名.bin}”
如果你有引号或转义符“\”,你会:
- “cp”
- “~/some/dir/{要重命名的文件.bin,\ 新文件名.bin}”
这也不是你想要的,除非“ new-name-of-file.bin”是你想要的新文件名。包括空格。由于首先发生括号扩展,然后发生波浪符号扩展,因此 bash 将执行:
- “cp”
- “/path/to/home/some/dir/my-file-to-rename.bin”
- “/path/to/home/some/dir/\ 新文件名.bin”
只需删除空间即可解决所有问题。