据我所知,安全且可移植的文件名可以仅由 aA-zZ 0-9 连字符和下划线组成。同时,如果我们摆脱安全的文件命名习惯,我们可以使用诸如!
?
$
%
"
'
/
等字符。
我的问题是关于引号,即"
和'
。它们不是比$
或更危险吗/
?例如,如果文件名包含空格,而我们需要复制这样的文件,我们会将其名称括在引号中:
cp "file with spaces" folder_without_spaces
正因为如此,我质疑自己:文件名中的引号有时是否会与 shell 代码中的引号混淆?
例如,你有
aaa aaa ccc.txt
aaa "xxx" ccc.txt
然后你使用
for f in *.txt; do mv "$f" backup_dir; done
然后“砰!”——有东西坏了。实际上在这个特定示例中没有任何东西坏了(至少在 Zsh 上没有),但我希望您明白我的意思。
不。
for f in *.txt; do mv "$f" backup_dir; done
不会破坏(*)。我会说我在其他地方说过的话:现代 shell 是编程语言,而不是宏处理器,即 shell 不会仅仅$f
在那里扩展为文本,以进一步处理引号,而只是在内部按原样传递值。这类似于print(foo)
Python 中的某些东西,即使foo
是包含 的字符串也是安全的sys.exit(1)
。事实是,在(类似 POSIX 的 shell)中,对未加引号
$var
的字段进行拆分会产生这样的结果:对于简单值,其结果看起来就像在命令行上粘贴原始值时得到的结果,但这是一个单独的函数,也不会处理扩展结果中的引号。字段拆分仅适用于空格(或任何设置
IFS
为的字符),例如var='"foo bar"'; mv $var /tmp
传递mv
三个参数,,,,按空格拆分并将引号视为常规字符。它与完全不同。"foo
bar"
/tmp
mv "foo bar" /tmp
未加引号的扩展也会经过文件名通配,因此例如
?
或*
(和[...]
)可能会在那里引起问题。看:
当然,zsh 在这里脱离了 POSIX 兼容性,不会对变量扩展进行字段拆分和通配。因此在 zsh 中
mv $f backup_dir
也应该是安全的(*)。星号表示如果您的目录包含以破折号开头的文件名,
mv
则会尝试将其解析为选项。shell 引用对此无能为力,因为程序只是将命令行参数作为字符串获取,它不知道它们来自哪里。(例如,名为 的文件
-v.txt
不会被移动,而是会被视为-v
详细模式的选项,而无效-.
选项会产生错误。整个命令也会缺少目标文件名。)解决方案是始终以 开头相对路径名
./
,或用 将命令行选项与文件名分开--
。或者两者兼而有之,举个例子: