假设我有一个包含以下文件/目录的目录:
google
apple
mozilla foundation # a file with spaces
browsers
所以我想将文件移动到browsers
目录中。这是我写的一个脚本:
for d in $(ls); do
if ! [ "$d" == "browsers" ]; then
mv "$d" "browsers"
fi
done
然后google
去apple
了,browsers
但我得到了:
mv: cannot stat 'mozilla': No such file or directory
mv: cannot stat 'foundation': No such file or directory
显然,问题出在变量中的空格。处理这个问题的正确方法是什么?
当然,我相信应该有一个更优雅的单行命令来执行此操作,但我想知道应该如何使用带有空格的变量。
答案1
做不是曾经使用过:
for d in $(ls)
使用这个代替:
for d in *
使用 时存在的两个问题$(ls)
是 shell 将 的结果服从$(ls)
于分词和路径名扩展。就你而言,它是分词这导致 mozilla foundation
变成mozilla
和foundation
。
有关不使用原因的详细讨论$(ls)
,请参阅“做什么$(ls *.txt)
?”
如果您需要考虑目录中没有非隐藏文件的情况(这种情况可能看起来更好,因为它不会在循环中执行任何传递,for i in $(ls)
而不是在循环中执行一次传递(*
除了在)) 中,您希望告诉 shell 不要扩展到任何不匹配的 glob:$i
for i in *
zsh
zsh
:for i in *(N)
ksh93
:for i in ~(N)*
bash4.4+
:f() { local -; shopt -s nullglob; for i in *; ...; done; }; f
(重点是nullglob
在本地使用该选项(另请参阅该选项以了解与默认选项failglob
类似的行为))。zsh
yash
:(然后重置,据我所知,set -o nullglob
没有本地范围的选项)yash
- 在其他 shell 中,您始终可以
[ -e "$i" ] || [ -L "$i" ] || continue
在循环中添加 a 来检查文件是否存在。
答案2
不解析ls
结果,除了 John1024 的答案之外,您还可以使用find
.
LC_ALL=C find . ! -name . -prune ! -name '.*' -type f -exec mv -t /path/to/dest {} +
(这里假设使用 GNUfind
选项-t
,跳过隐藏文件,如ls
或*
glob 并排除非常规文件。请注意,该列表未排序(与ls
或相反*
)。LC_ALL=C
需要正确跳过名称以.
这些开头的任何文件不包含用户区域设置中的有效字符,但这会影响错误消息的语言)。