双引号与Bash中的星号文件名扩展

时间:2013-03-16 23:05:51

标签: bash quotes asterisk expansion

在目录~/temp/a/foo/~/temp/b/foo foo/中,我有一些名为bar1bar2bar bar1bar 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/*了解?

3 个答案:

答案 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。我自己从不使用包含空格的文件名,只是为了避免像你现在面临的那样复杂化。