5

在目录中~/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一个"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/*以便cp理解?

4

3 回答 3

4

for循环不仅是错误的——sed也不是这项工作的正确工具。

while IFS= read -r -d '' dir; do
  cp "$dir" "${dir/foo/}"
done < <(find . -type d -name '*foo' -print0)

-print0find(and ) 上使用IFS= read -r -d ''可确保带有换行符的文件名不会弄乱你。

使用该< <(...)构造可确保如果循环内部设置变量、更改目录状态或执行类似操作,这些更改将继续存在(管道的右侧位于 bash 的子外壳中,因此管道进入 while 循环将意味着在该while循环内对shell状态所做的任何更改都将在退出时被丢弃)。

using${dir/foo/}比 invoking 效率高得多sed,因为它在 bash 内部进行字符串替换。

于 2013-03-16T23:36:56.047 回答
2

这里的问题不是 with cp,而是 with for,因为默认情况下,它按单词而不是目录名称拆分子shell的输出。

一个懒惰的解决方法是使用while并逐行处理目录列表,如下所示:

find . -type d -name '*foo' | while read dir; do cp "$dir"/* "$(echo $dir | sed 's_foo__g')" ; done

这应该可以解决您的空格问题,但这绝不是一个万无一失的解决方案。

有关更准确的解决方案,请参阅 Charles Duffy 的答案。

于 2013-03-16T23:28:15.013 回答
0

重命名~/temp/b/foo foo/为不带空格的名称,例如~/temp/b/foo_foo/,再次执行您尝试执行的操作。之后,如果您确实需要,您可以将其重命名为原始名称,并带有空格。顺便提一句。我自己从不使用包含空格的文件名,只是为了避免像您现在面临的那样复杂。

于 2013-03-16T23:13:30.127 回答