2013-03-16 95 views
5

在目录~/temp/a/foo/~/temp/b/foo foo/我有一些名为bar1bar2bar bar1bar bar2双引号VS星号文件名扩展中的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能够理解?

+0

请参阅http://mywiki.wooledge.org/DontReadLinesWithFor – 2013-03-16 23:34:27

回答

4

不仅for环路错误 - sed也不适合此工作。

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

find(和IFS= read -r -d '')使用-print0确保与新行的文件名不会弄乱你。

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

使用${dir/foo/}比调用sed更有效,因为它在bash内部进行字符串替换。

+0

你确定需要'IFS ='吗?伟大的职位btw! – janos 2013-03-16 23:41:04

+0

尽管janos建议接受这个答案作为解决方案,但事实证明,代码实际上并没有移动任何文件。 Bash 4.2.24抱怨cp不能统计''。我不是100%确定'cp“$ dir”“$ {dir/foo /}”'这行是干什么的。它真的试图将文件复制到'foo'目录上的现有目录吗? – uvett 2013-03-17 00:13:25

+0

不应该'读取...文件'是'read ... dir'? – 2013-03-17 00:14:54

0

~/temp/b/foo foo/重命名为没有空格的内容,例如~/temp/b/foo_foo/并做你正在尝试做的事。之后,如果您真的需要,可以使用空格将其重命名为原始文件。 BTW。我自己从来没有使用含有空格的文件名,只是为了避免像现在面临的那样复杂。

+0

我试图简化一个涉及数十个文件夹和数百个带有难看名字的文件的问题。重命名它们看起来像是次优选项,特别是因为我将不得不稍后恢复文件夹名称。 – uvett 2013-03-16 23:18:08

2

这里的问题不是cp,而是for,因为默认情况下,它会通过单词而不是目录名来分割subshel​​l的输出。

懒惰的解决方法是通过线使用while而不是处理目录线名单如下:

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

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

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

+0

几乎是正确的,但是您确实应该NUL分隔管道的内容 - 否则,包含换行符的恶意制作的文件名可能导致任意文件被复制。 – 2013-03-16 23:35:01

+0

...顺便说一下,'dir'永远不会包含空格作为'for'工作方式的工件;单词拆分不是for语义的一部分,如果'IFS'被设置为不包含空格,则string-split输出_can_包含空格。也就是说,依赖于字符串分割的原因是错误的(例如,因为glob扩展同时发生)。 – 2013-03-16 23:40:11

+0

谢谢,这似乎适用于我所有的真实世界的文件复制问题。它在我简化的例子中实际上失败了,是一个有趣的细节。 (sed删除'foo foo'文件夹名称中的'foo')。 – uvett 2013-03-16 23:45:56