Bash:递归复制命名文件,保留文件夹结构


103

我希望:

cp -R src/prog.js images/icon.jpg /tmp/package

将在目标目录中产生对称结构:

/tmp
|
+-- package
    |
    +-- src
    |   |
    |   +-- prog.js
    |
    +-- images
        |
        +-- icon.jpg

但是相反,两个文件都被复制到/ tmp / package中。平面副本。(这是在OSX上)。

我是否可以使用一个简单的bash函数将所有文件(包括通配符指定的文件(例如src / *。js))复制到目标目录中的正确位置。有点像“为每个文件运行mkdir -p $(dirname "$file"); cp "$file" $(dirname "$file")”,但也许只有一个命令。

这是一个相关的线程,这表明不可能。不过,作者的解决方案对我而言并没有那么有用,因为我只想提供文件列表(带或不带通配符),并将所有文件复制到目标目录。IIRC MS-DOS xcopy可以执行此操作,但是cp似乎没有等效功能。

Answers:


153

您是否尝试过使用--parents选项?我不知道OS X是否支持,但是可以在Linux上运行。

cp --parents src/prog.js images/icon.jpg /tmp/package

如果这在OS X上不起作用,请尝试

rsync -R src/prog.js images/icon.jpg /tmp/package

作为建议。


4
谢谢。原来在Mac上不可能使用“ cp --parents”,但是很高兴知道其他unixen的标志。rsync -R是解决此问题的最简单的便携式解决方案。
mahemoff

1
我接受了它的优雅/可记忆性,但是发现它并不能复制整个目录(至少在OSX上),而下面的tar可以。
mahemoff 2012年

cp --parents在OSX(BSD cp)中是非法选项,但gcp(GNU cp)可以正常工作。如果它不在您的系统中,请使用brew install coreutils。您将使用g前缀的许多工具。
kyb

@mahemoff cp -R --parentsrsync -rR相对地复制文件和目录。
Vortico

22

单程:

tar cf - <files> | (cd /dest; tar xf -)

哦,我比我的回答要好得多。
EMPraptor

4
您也可以使用该-C选项为您执行chdir- tar cf - _files_ | tar -C /dest xf -或类似的操作。
D.Shawley,2009年

谢谢,这很简洁,尽管为了简单起见,我更喜欢rsync。
mahemoff

1
大!有人知道如何将其转换为shell命令吗?它应该接受N个输入,第一个N-1是要复制的文件,最后一个是目标文件夹。
2012年

@arod $ {!#}是最后一个参数,并使用它来获取前面的参数stackoverflow.com/questions/1215538/…。如果您编写命令,请在此处链接到要点。
mahemoff 2012年

18

另外,如果您是老式学校,请使用cpio:

cd /source;
find . -print | cpio -pvdmB /target

显然,您可以根据自己的意愿过滤文件列表。

“ -p”选项用于“直通”模式(与输入时使用“ -i”或输出时使用“ -o”相反)。'-v'是冗长的(在处理文件时列出它们)。“ -m”保留修改时间。“ -B”表示使用“大块”(大块是5120字节而不是512字节);这些天可能无效。


1
最好-print0--nulloption 组合使用,以免find . -print0 | cpio -pvdmB --null /target
打乱

称它为老式的,可移植的,随便什么,但是我绝对相信cpio这项任务。我确实同意应使用-print0and -null选项,否则,在某些时候,有人会给您一些带有“特殊字符”的文件夹(最有可能是空格),并且会发生某些情况。我并不是从个人经验出发,但是您可能会尝试备份一堆文件,但由于文件名中包含空格,最终只能备份其中一半。(好吧,我是根据个人经验讲的。)
bballdave025

@ bballdave025:cpio如果文件名包含换行符,您只会遇到如图所示的问题-然后通常会收到一条错误消息,提示找不到文件名中每个换行符的两个(或更多)文件名。(有时,您收到的消息可能会更少-但这在构造测试用例时需要格外小心。)当我使用时cpio,别无选择--null。双破折号选项不是SVR4选项符号的一部分,并且在这两个概念中-print0都不存在find。但这是很久以前的事(例如,在90年代中期。在Linux占据统治地位之前)。
乔纳森·莱夫勒

感谢您的详细信息,@Jonathan_Leffler。我喜欢在这里学习东西。现在,我试图记住我犯了什么递归复制错误,这给我空间带来了麻烦-我有很多方法可以犯该错误,
bballdave025

@ bballdave025-一种可能是xargs在混合中使用-它在空白处分割-空白,制表符,换行符。太太,我不确定你会怎么做或为什么要这么做。cpio在输出模式下的GNU手册每行只有一个文件名。SVR4用户手册(印刷于1990年)含糊不清:(cpio -o复制模式)读取标准输入以获得路径名列表,并将这些文件以及路径名和状态信息一起复制到标准输出中。因此,它有可能在空格处打断名字。
乔纳森·莱夫勒

16

rsync的-R选项将达到您的期望。这是一个功能非常丰富的文件复印机。例如:

$ rsync -Rv src/prog.js images/icon.jpg /tmp/package/
images/
images/icon.jpg
src/
src/prog.js

sent 197 bytes  received 76 bytes  546.00 bytes/sec
total size is 0  speedup is 0.00

样本结果:

$ find /tmp/package
/tmp/package
/tmp/package/images
/tmp/package/images/icon.jpg
/tmp/package/src
/tmp/package/src/prog.js


1

尝试...

for f in src/*.js; do cp $f /tmp/package/$f; done

所以对于你原来的工作

for f in `echo "src/prog.js images/icon.jpg"`; do cp $f /tmp/package/$f; done

要么

v="src/prog.js images/icon.jpg"; for f in $v; do cp $f /tmp/package/$f; done

您不需要echo$v在这里。如果目标中不存在相应的目录,则此方法也会失败。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.