ChatGPT解决这个技术问题 Extra ChatGPT

如何使用“cp”命令排除特定目录?

我想复制目录中的所有文件,但特定子目录中的某些文件除外。我注意到 cp 命令没有 --exclude 选项。那么,我该如何实现呢?

tar -c | tar -x ?
@mvds,同意你的看法,使用 tar 和 '--exclude' 是个好主意。

s
syntagma

rsync 快速简单:

rsync -av --progress sourcefolder /destinationfolder --exclude thefoldertoexclude

您可以多次使用 --exclude

rsync -av --progress sourcefolder /destinationfolder --exclude thefoldertoexclude --exclude anotherfoldertoexclude

请注意,--exclude 选项之后的 dir thefoldertoexclude 是相对于 sourcefolder 的,即 sourcefolder/thefoldertoexclude

您还可以添加 -n 进行试运行以查看在执行实际操作之前将复制的内容,如果一切正常,请从命令行中删除 -n


同意,您无法超越 --exclude 的简单性和强大功能
thefoldertoexclude 是相对于 sourcefolder 还是当前工作目录?谢谢
它相对于源文件夹。这将排除文件夹 source/.git 被复制。 rsync -r --exclude '.git' 源目标
也许我错了,但我认为在“参数”之前添加“开关”是一个好习惯。 rsync 的手册页也报告了 --exclude 可与“=”语法一起使用或不使用。因此,为了跨操作系统标准化,我会使用 rsync -av --progress --exclude="thefoldertoexclude" sourcefolder /destinationfolder - 无论如何都赞成 rsync 而不是 find,因为您可以轻松地使用绝对路径作为源,而在 find 中它使用 {} 更棘手目的地
我认为斜线在错误的地方,rsync -av --progress sourcefolder/ destinationfolder --exclude thefoldertoexclude
C
Community

好吧,如果每个 unix-ish 文件实用程序(如 cp、mv、rm、tar、rsync、scp、...)都必须排除某些文件名模式,就会产生巨大的重复劳动。相反,这些事情可以作为 globbing 的一部分来完成,即通过您的 shell。

重击

man 1 bash/ extglob

例子:

$ shopt -s extglob
$ echo images/*
images/004.bmp images/033.jpg images/1276338351183.jpg images/2252.png
$ echo images/!(*.jpg)
images/004.bmp images/2252.png

因此,您只需在 !() 中放置一个模式,它就会否定匹配。该模式可以是任意复杂的,从枚举单个路径(如 Vanwaril 在另一个答案中所示)开始:!(filename1|path2|etc3),到带有星号和字符类的类似正则表达式的东西。有关详细信息,请参阅手册页。

zsh

man 1 zshexpn/ 文件名生成

您可以执行 setopt KSH_GLOB 并使用类似 bash 的模式。或者,

% setopt EXTENDED_GLOB
% echo images/*
images/004.bmp images/033.jpg images/1276338351183.jpg images/2252.png
% echo images/*~*.jpg
images/004.bmp images/2252.png

所以 x~y 匹配模式 x,但排除模式 y。再一次,有关完整的详细信息,请参阅联机帮助页。

新鱼!

fish shell 有一个 much prettier answer

🐟 cp (string match -v '*.excluded.names' -- srcdir/*) destdir

奖金专业提示

输入 cp *,点击 CtrlX* 看看会发生什么。 它没有害处我保证


@MikhailGolubtsov 可能是因为 globbing 不是递归的,并且一次只能工作一个级别。编辑出来。 PS:虽然它在 zsh 中有效。
不错的专业提示!这样您就可以轻松删除单个项目。非常感谢!
顺便说一句,要关闭 Bash 中的扩展模式匹配功能,请运行 setopt -u extglob
“......巨大的重复工作......”它不应该只是一个单行:从列表中排除匹配正则表达式的路径吗?像 cp 这样的文件操作实用程序如何不支持这个开箱即用的最简单直接的用例,这超出了我的理解。不过感谢您的提示!
@ayorgo 好吧,是的,它“应该”——但在 C 中,oneliner 不能做太多事情:将一些整数相乘并可能移动一个指针,就是这样。即使忽略源代码级别,C 中的正则表达式匹配也涉及额外的库依赖和额外的机器代码输出——现在将其乘以命令的数量,你就会得到不平凡的(无限的?...)开销。至少这就是我理解为什么它被“重构”到 shell 的原因;我完全可以理解它低于标准的 UI 方面,但希望你现在也能看到技术上的理由。最好的祝愿!
L
Linus Kleen

为什么在可以执行以下操作时使用 rsync

find . -type f -not -iname '*/not-from-here/*' -exec cp '{}' '/dest/{}' ';'

这假设目标目录结构与源目录结构相同。


我认为您需要 -path 参数来测试路径层次结构,而不是 -iname
最后还需要一个分号:find . -type f -not -path '*/not-from-here/*' -exec cp '{}' '/dest/{}' \;
哇,它不会让我:“编辑必须至少有 6 个字符”!
@MatthewWilcoxson Meh。只要您获得更多代表,这些限制就会被解除。我相应地编辑了答案。再次感谢!
@Henning 为什么不rsync?因为它可能不存在于系统中!而 findcp 总是在他们的位置上。或者你来自那种安装 2gigs 的东西来做简单事情的人?
p
pts
cp -r `ls -A | grep -v "c"` $HOME/

在 Windows 10 .sh 中为我工作
制作了一个 shell 函数,可简化自定义源路径的使用并仅排除一个文件或目录:# $1 = source path # $2 = destination path # $3 = filter copy_from_source_to_destination_except_filter() { cp -r $(ls -A $1 | grep -v -w $3 | awk -v path=$1 '{printf "%s/%s ", path, $1}') $2 }
带有空格的目录失败
@Sérgio 我尚未对其进行测试,但 cp -r "$(ls -A | grep -v "c")" $HOME/ 应该可以工作。答案中的命令失败,因为 cpls -A | grep -v "c" 的输出进行操作,该输出未加引号,因此在空格处中断。 "$(…)""`…`" 相同,但更容易理解。
o
ostergaard

我找到的最简单的方法是,您只需在括号中添加文件和文件夹的名称即可复制所有文件(不包括文件和文件夹):

shopt -s extglob
cp -r !(Filename1 | FoldernameX | Filename2) Dest/

对我不起作用。我得到-bash: !: event not found
shopt -s extglob(执行此以启用!在 cp、rm 和其他中)
@geneorama 如果启用了历史替换,则会发生这种情况。 serverfault.com/a/208414/352016
不错的提示,但它在 sh 中不起作用。
S
Skandix

它是相对于源目录的。
这将排除目录 source/.git 被复制。

rsync -r --exclude '.git' source target

与最佳答案相比有什么区别/改进?
@reducingactivity 较少过时的标志
我觉得第一个答案中的 '-a' 比普通的旧 -r: explainshell.com/explain?cmd=rsync+-a
@reducingactivity 没什么,但很容易消化,因为表达式更短,只是我个人的喜好
C
Community

扩展 mvds’s comment,这对我有用

cd dotfiles
tar -c --exclude .git --exclude README . | tar -x -C ~/dotfiles2

tar 的好处是,您可以使用 exclude.tag 文件来忽略目录 stackoverflow.com/a/13280610/722796gnu.org/software/tar/manual/html_node/exclude.html
L
LeOn - Han Li

rsync 实际上非常棘手。必须进行多次测试才能使其正常工作。

假设您想将 /var/www/html 复制到 /var/www/dev 但需要排除 /var/www/html/site/video/ 目录可能是由于它的大小。命令将是:

rsync -av --exclude 'sites/video' /var/www/html/ /var/www/dev

一些警告:

源中最后一个斜杠/是必需的,否则它也会复制源目录而不是它的内容,变成/var/www/dev/html/xxxx,这可能不是你想要的。 --exclude 路径直接相对于源。即使您放置完整的绝对路径,它也不起作用。 -v 用于详细,-a 用于存档模式,这意味着您想要递归并想要保留几乎所有内容。


一个处理特殊字符和空格的简单解决方案
感谢您解释参数,与当前的最佳答案不同!
要排除多个文件夹怎么样?
@ddzzbbwwmm 您现在可能已经想通了,但是为了后代:您可以添加多个 --exclude 标志,例如:--exclude 'foo' --exclude 'bar'
s
sudo work
cp -rv `ls -A | grep -vE "dirToExclude|targetDir"` targetDir

编辑:忘记排除目标路径(否则它会递归复制)。


注意包含空格的目录条目。
d
dzon

rsync

rsync -r --verbose --exclude 'exclude_pattern' ./* /to/where/

并首先尝试使用 -n 选项来查看将要复制的内容


与最佳答案相比有什么区别/改进?
V
Vanwaril

我假设您使用的是 bash 或 dash。这行得通吗?

shopt -s extglob  # sets extended pattern matching options in the bash shell
cp $(ls -laR !(subdir/file1|file2|subdir2/file3)) destination

执行 ls 排除您不想要的文件,并将其用作 cp 的第一个参数


您可以跳过额外的 ls,直接执行 cp !(file1|file1) dest
不要使用 -laR。它添加了干扰cp的字符串。 cp $(ls folder/!exclude_folder0|exclude_folder1)) dest
e
ehudokai

另一个更简单的选择是安装和使用具有 --exclude-dir 选项的 rsync,它可以用于本地和远程文件。


k
kungfooman

只需将其临时移动到隐藏目录中(如果需要,可以在之后重命名)。

mkdir .hiddendir
cp * .hiddendir -R
mv .hiddendir realdirname

可能不太好——但这是我在这里找到的唯一一个适用于 cp 和标准 POSIX shell(如 sh)的选项。
这个答案被严重低估了。这是最兼容,最容易阅读和易于理解的答案。赞,我不知道为什么我没有想到它。
谢谢@RobertTalada,答案离现在还有多远? ‎️‍🌈
明显的缺点是您可能会避免复制某些内容,因为它太大了。
M
Milan Simek

这是对 Linus Kleen 答案的修改。他的回答对我不起作用,因为会有 .添加在 cp 不喜欢的文件路径前面(路径看起来像 source/.destination/file)。

这个命令对我有用:

find . -type f -not -path '*/exlude-path/*' -exec cp --parents '{}' '/destination/' \;

--parents 命令保留目录结构。


z
zyfyy
cp -r `ls -A | grep -v "Excluded_File_or_folder"` ../$target_location -v

P
Panduka

rsync 对我们不可用。下面是一个可行的替代方案。

tar -cf - --exclude='./folder' --exclude='./file.tar' ./source_directory | tar -xf - -C ./destination_directory

p
pts
mv tobecopied/tobeexcluded .
cp -r tobecopied dest/
mv tobeexcluded tobecopied/

L
LouisXW
ls -I "filename1" -I "filename2" | xargs cp -rf -t destdir 

第一部分 ls 所有文件,但隐藏带有标志 -I 的特定文件。 ls 的输出用作第二部分的标准输入。 xargs 从标准输入构建并执行命令 cp -rf -t destdir。标志-r表示递归复制目录,-f表示强制复制文件,这将覆盖destdir中的文件,-t指定目标目录复制到。


q
qräbnö

晚了10年。归功于莱纳斯·克莱恩。

我讨厌rsync! ;) 那么为什么不使用 findcp 呢?并通过此答案mkdir创建一个不存在的文件夹结构。

cd /source_folder/ && find . -type d -not -path '*/not-from-here/*' -print -exec mkdir -p '/destination_folder/{}' \;

cd /source_folder/ && find . -type f -not -path '*/not-from-here/*' -print -exec cp -au '{}' '/destination_folder/{}' \;

看起来 cd 有必要将相对路径与 find 连接起来。

mkdir -p 将创建所有子文件夹,并且在文件夹已存在时不会报错。

豪斯滕我们有下一个问题。当有人创建一个中间有一个新文件的新文件夹时会发生什么?确切地说:这些新文件将失败。 (解决方案:再次运行它!:))将所有内容放入一个 find 命令的解决方案似乎很困难。

清理:https://unix.stackexchange.com/q/627218/239596


试一试。您的答案的当前版本在第一步目录中创建。由于此步骤当前包含 -not -path '*/not-from-here/*',它将创建目录 ./not-from-here。可能,这不是故意的。因此,对于第一步(目录创建),您可能需要 -not -path '*/log'
y
yanana

我使用“do while”循环来读取 find 命令的输出。在此示例中,我匹配(而不是排除)某些模式,因为我想要的模式匹配数量比我不想要的要少。您可以在 -iname 标志前使用 -not 反转逻辑:

find . -type f -iname "*.flac" -o -print0 -iname "*.mp3" -print0 -o -iname "*.wav" -print0 -o -iname "*.aac" -print0 -o -iname "*.wma" -print0 | while read -d $'\0' file; do cp -ruv "$file" "/media/wd/network_sync/music/$file"; done

我使用上述方法复制了我的服务器上的所有音乐类型文件,这些文件比我安装在 /media/wd 的 Western Digital TV Live Hub 上的文件新。我之所以使用上面是因为我有很多 DVD 文件、mpeg 等要排除,并且由于某种原因 rsync 看起来像是在复制,但是在我查看 wd 设备之后,尽管没有文件,但这些文件不存在使用此命令进行 rsync 期间的错误:

rsync -av --progress --exclude=*.VOB --exclude=*.avi --exclude=*.mkv --exclude=*.ts --exclude=*.mpg --exclude=*.iso --exclude=*ar --exclude=*.vob --exclude=*.BUP --exclude=*.cdi --exclude=*.ISO --exclude=*.shn --exclude=*.MPG --exclude=*.AVI --exclude=*.DAT --exclude=*.img --exclude=*.nrg --exclude=*.cdr --exclude=*.bin --exclude=*.MOV --exclude=*.goutputs* --exclude=*.flv --exclude=*.mov --exclude=*.m2ts --exclude=*.cdg --exclude=*.IFO --exclude=*.asf --exclude=*.ite /media/2TB\ Data/data/music/* /media/wd/network_sync/music/