在目录~/temp/a/foo/
和~/temp/b/foo foo/
中,我有一些名为bar1
,bar2
,bar bar1
,bar bar2
等的文件。
我正在尝试编写一行Bash,将包含“foo”的目录中的所有这些文件作为名称的最后一部分复制到相应“foo”文件夹上方的文件夹中。
只要文件名中没有空格,这是一项简单的任务,但在处理foo foo
目录时,以下两个命令失败:
for dir in `find . -type d -name '*foo'` ; do cp $dir/* "$(echo $dir|sed 's_foo__g')" ; done
(cp
命令无法将foo
的{{1}}视为同一目录名的一部分。)
"foo foo"
(for dir in `find . -type d -name '*foo'` ; do cp "$dir/*" "$(echo $dir|sed 's_foo__g')" ; done
未展开。)
尝试将"$dir/*"
替换为$dir/*
的尝试更不成功。
是否有一种简单的方法可以展开"$(echo $dir/*)"
以便$dir/*
了解?
答案 0 :(得分:4)
不仅for
循环错误 - sed
也不适合此工作。
while IFS= read -r -d '' dir; do
cp "$dir" "${dir/foo/}"
done < <(find . -type d -name '*foo' -print0)
在-print0
(和find
)上使用IFS= read -r -d ''
可确保带有换行符的文件名不会让您感到麻烦。
使用< <(...)
构造确保如果循环内部设置变量,更改目录状态或执行类似的操作,那些更改将继续存在(管道的右侧位于bash的子shell中) ,所以管道进入while循环意味着在while循环中进行的shell状态的任何更改都会在其退出时被丢弃。
使用${dir/foo/}
比调用sed
更有效率,因为它在bash内部执行字符串替换。
答案 1 :(得分:2)
此处的问题不在于cp
,而在于for
,因为默认情况下它会按字词而不是目录名分割子shell的输出。
一个懒惰的解决方法是使用while
代替并逐行处理目录列表:
find . -type d -name '*foo' | while read dir; do cp "$dir"/* "$(echo $dir | sed 's_foo__g')" ; done
这应该可以解决你的空间问题,但这绝不是一个万无一失的解决方案。
请参阅Charles Duffy的答案,以获得更准确的解决方案。
答案 2 :(得分:0)
将~/temp/b/foo foo/
重命名为没有空格的内容,例如~/temp/b/foo_foo/
并做你想再做的事。之后,如果你真的需要,你可以将它重命名为原始空间。 BTW。我自己从不使用包含空格的文件名,只是为了避免像你现在面临的那样复杂化。