递归复制文件夹,排除部分文件夹
Posted
技术标签:
【中文标题】递归复制文件夹,排除部分文件夹【英文标题】:Copy folder recursively, excluding some folders 【发布时间】:2011-01-12 17:01:26 【问题描述】:我正在尝试编写一个简单的 bash 脚本,它将文件夹的全部内容(包括隐藏文件和文件夹)复制到另一个文件夹中,但我想排除某些特定文件夹。我怎样才能做到这一点?
【问题讨论】:
我想像 find 。 -name * 通过管道传输到 grep /v "exclude-pattern" 以过滤您不想要的那些,然后通过管道传输到 cp 进行复制。 我试图做类似的事情,但无法弄清楚如何使用 cp 和管道 这应该交给超级用户。您要查找的命令是 xargs。你也可以做类似两个 tar 通过管道连接的事情。 也许为时已晚,它不能准确回答问题,但这里有一个提示:如果您只想排除目录的直接子级,您可以利用 bash 模式匹配,例如cp -R !(dir1|dir2) path/to/destination
注意!(dir1|dir2)
模式需要extglob
开启(shopt -s extglob
开启)。
【参考方案1】:
简单的解决方案(但我仍然更喜欢*** cmets 的 bash 模式匹配):
touch /path/to/target/.git
cp -n -ax * /path/to/target/
rm /path/to/target/.git
这利用了cp
的-n
选项,强制cp
不覆盖现有目标。
缺点:适用于 GNU cp
。如果您没有 GNU cp
,那么 cp
操作可能会返回错误代码 (1
),这很烦人,因为您无法判断这是否是真正的失败。
【讨论】:
【参考方案2】:? 快速入门
运行:
rsync -av --exclude='path1/in/source' --exclude='path2/in/source' [source]/ [destination]
? 备注
-avr
将创建一个名为 [destination]
的新目录。
source
和 source/
创建不同的结果:
source
— 将源的内容复制到目标。
source/
— 将文件夹源复制到目标。
要排除许多文件:
--exclude-from=FILE
— FILE
是包含要排除的其他文件或目录的文件的名称。
--exclude
也可能包含通配符:
例如--exclude=*/.svn*
修改自:https://***.com/a/2194500/749232
? 例子
起始文件夹结构:
.
├── destination
└── source
├── fileToCopy.rtf
└── fileToExclude.rtf
运行:
rsync -av --exclude='fileToCopy.rtf' source/ destination
结束文件夹结构:
.
├── destination
│ └── fileToExclude.rtf
└── source
├── fileToCopy.rtf
└── fileToExclude.rtf
【讨论】:
[REVIEW] 不错的布局 Jack.. 但是代码已经在 10 年前提交了 :d 所以我不得不投反对票,尽管你已经发布了创意图标! @Goodies 当我阅读原始帖子时,我对布局和解释感到困惑,所以我重新格式化了它(感谢您欣赏图标和布局!)。如果我想改进原始答案,我应该尝试编辑它(十年前的那个)吗?我只是发现修改需要一段时间才能获得批准,而且我认为我提交的内容非常独特,足以被视为与众不同。 正确.. 支持您的评论.. 顺便说一句,我是一个初学者模型.. 我可以编辑东西,但我只对最近的标题进行此操作。除非确实有错误,否则最好保持问题和答案布局完整。【参考方案3】:受@SteveLazaridis 的回答的启发,这将失败,这是一个 POSIX shell 函数 - 只需复制并粘贴到您 $PATH
中名为 cpx
的文件中并使其可执行 (chmod a+x cpr
)。 [源代码现在保存在我的GitLab。
#!/bin/sh
# usage: cpx [-n|--dry-run] "from_path" "to_path" "newline_separated_exclude_list"
# limitations: only excludes from "from_path", not it's subdirectories
cpx()
# run in subshell to avoid collisions
(_CopyWithExclude "$@")
_CopyWithExclude()
case "$1" in
-n|--dry-run) DryRun='echo'; shift; ;;
esac
from="$1"
to="$2"
exclude="$3"
$DryRun mkdir -p "$to"
if [ -z "$exclude" ]; then
cp "$from" "$to"
return
fi
ls -A1 "$from" \
| while IFS= read -r f; do
unset excluded
if [ -n "$exclude" ]; then
for x in $(printf "$exclude"); do
if [ "$f" = "$x" ]; then
excluded=1
break
fi
done
fi
f="$f#$from/"
if [ -z "$excluded" ]; then
$DryRun cp -R "$f" "$to"
else
[ -n "$DryRun" ] && echo "skip '$f'"
fi
done
# Do not execute if being sourced
[ "$0#*cpx" != "$0" ] && cpx "$@"
示例用法
EXCLUDE="
.git
my_secret_stuff
"
cpr "$HOME/my_stuff" "/media/usb" "$EXCLUDE"
【讨论】:
在不解释问题出在哪里以及如何解决的情况下说某人的答案“会失败”似乎是无益的...... @underscore_d :是的,事后看来,尤其是因为我现在不记得失败了:-( 多件事:(1)它多次复制文件,(2)逻辑仍然复制要排除的文件。使用 i=foo 运行循环:它将被复制 3 次而不是 4 次用于任何其他文件,例如i=test.txt。 感谢@EricBringley 澄清史蒂夫回答的缺点。 (他确实说它未经测试。)【参考方案4】:使用 rsync:
rsync -av --exclude='path1/to/exclude' --exclude='path2/to/exclude' source destination
请注意,使用 source
和 source/
是不同的。尾部斜杠表示将文件夹source
的内容复制到destination
。没有斜杠,表示将文件夹source
复制到destination
。
或者,如果您有很多目录(或文件)要排除,您可以使用--exclude-from=FILE
,其中FILE
是包含要排除的文件或目录的文件的名称。
--exclude
还可以包含通配符,例如--exclude=*/.svn*
【讨论】:
我建议添加 --dry-run 以检查要复制的文件。 @AmokHuginnsson - 你使用什么系统?我知道的所有主流 Linux 发行版都默认包含 Rsync,包括 RHEL、CentOS、Debian 和 Ubuntu,我相信它也在 FreeBSD 中。 对于 RHEL 派生发行版:yum install rsync,或在基于 Debian 的发行版上:apt-get install rsync。除非您是在自己的硬件上从绝对基础构建服务器,否则这不是问题。默认情况下,rsync 安装在我的 Amazon EC2 机器上,以及来自 ZeroLag 和 RackSpace 的机器上。 rsync 与 cp 相比似乎非常慢?至少这是我的经验。 例如忽略 git 目录:rsync -av --exclude='.git/' ../old-repo/ .
【参考方案5】:
使用 tar 和管道。
cd /source_directory
tar cf - --exclude=dir_to_exclude . | (cd /destination && tar xvf - )
您甚至可以在 ssh 中使用这种技术。
【讨论】:
这种方法不必要地首先解压目标源(并排除存档中的特定目录),然后在目标处解压。不推荐! @Waldheri 你错了。这是最好的解决方案。它完全符合 OP 的要求,并且适用于大多数 *nix 等操作系统的默认安装。去皮和去皮是即时完成的,没有文件系统人工制品(在内存中),这个 tar+untar 的成本可以忽略不计。 @WouterDonders Tar 开销最小。它不应用压缩。 当rsync
在您的容器中不可用并且您不想费心安装它时,这是完美的选择。【参考方案6】:
您可以将find
与-prune
选项一起使用。
来自man find
的例子:
【讨论】:
直接从手册页中定位高度相关示例的道具。 确实不错!这也可用in the online docs。不幸的是,cpio
还没有为 MSYS2 打包。【参考方案7】:
你可以使用 tar,加上 --exclude 选项,然后在目的地解压它。例如
cd /source_directory
tar cvf test.tar --exclude=dir_to_exclude *
mv test.tar /destination
cd /destination
tar xvf test.tar
查看 tar 的手册页了解更多信息
【讨论】:
【参考方案8】:EXCLUDE="foo bar blah jah"
DEST=$1
for i in *
do
for x in $EXCLUDE
do
if [ $x != $i ]; then
cp -a $i $DEST
fi
done
done
未经测试...
【讨论】:
这是不正确的。一些问题:正如所写,它会复制一个不应该被排除多次的文件(在这种情况下要排除的项目数是 4)。即使您确实尝试复制“foo”,即排除列表中的第一项,当您到达 x=bar 并且 i 仍然是 foo 时,它仍然会被复制。如果您坚持在没有预先存在的工具(例如 rsync)的情况下执行此操作,请将副本移动到 'for x in...' 循环之外的 if 语句,并使 'for x...' 循环更改逻辑语句if(true) 复制文件。这将阻止您多次复制。【参考方案9】:类似于 Jeff 的想法(未经测试):
find . -name * -print0 | grep -v "exclude" | xargs -0 -I cp -a destination/
【讨论】:
抱歉,但我真的不明白为什么有 5 个人在它未经测试且似乎无法进行简单测试的情况下对此表示赞同:我在/usr/share/icons
的子目录中尝试了这个,并且立即得到find: paths must precede expression: 22x22
,后者是其中的子目录之一。我的命令是find . -name * -print0 | grep -v "scalable" | xargs -0 -I cp -a /z/test/
(诚然,我在 MSYS2 上,所以真的在 /mingw64/share/icons/Adwaita
,但我看不出这是 MSYS2 的错)以上是关于递归复制文件夹,排除部分文件夹的主要内容,如果未能解决你的问题,请参考以下文章
TFS2008递归复制文件并不总是有效(编译vs2003)(AfterCompile目标)