递归复制文件夹,排除部分文件夹

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] 的新目录。 sourcesource/ 创建不同的结果: source — 将源的内容复制到目标。 source/ — 将文件夹源复制到目标。 要排除许多文件: --exclude-from=FILEFILE 是包含要排除的其他文件或目录的文件的名称。 --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

请注意,使用 sourcesource/ 是不同的。尾部斜杠表示将文件夹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的例子:

cd /源目录 找 。 -name .snapshot -prune -o \( \! -name *~ -print0 \)| cpio -pmd0 /dest-dir 此命令将 /source-dir 的内容复制到 /dest-dir,但省略 名为 .snapshot 的文件和目录(以及其中的任何内容)。它也是 省略名称以 ~ 结尾的文件或目录,但不包括它们的 con- 帐篷。构造 -prune -o \( ... -print0 \) 很常见。这 这里的想法是 -prune 之前的表达式匹配的是 被修剪。但是,-prune 操作本身返回 true,因此 跟随 -o 确保仅评估右侧 那些没有被修剪的目录(修剪的内容 甚至没有访问过目录,因此它们的内容无关紧要)。 -o 右侧的表达式仅在括号中 为了清楚起见。它强调 -print0 动作只发生 对于没有应用 -prune 的东西。因为 测试之间的默认“和”条件比 -o 绑定得更紧密,这 无论如何都是默认值,但括号有助于显示正在发生的事情 在。

【讨论】:

直接从手册页中定位高度相关示例的道具。 确实不错!这也可用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目标)

linux文件夹内文件复制,如何排除某个文件

linux文件夹内文件复制,如何排除某个文件

如何在 git 中列出所有排除的文件? [复制]

从 Google Cloud Storage 复制时排除特定文件或目录

从 git 存储库中排除 ios 项目的哪些文件? [复制]