仅当目录包含指定文件时才在 linux 中复制目录

Chi*_*lax 4 command-line cp

我需要复制 /parent 下的所有文件夹(到新位置),但前提是它有 123.dat - 在这种情况下,我也需要复制该文件夹,但不复制它包含的任何其他文件。

所以这个:

|parent
|    |a
|    |    123.dat
|    |    456.dat
|    |b
|    |    123.dat
|    |    789.dat
|    |c
|    |    456.dat
|    |    789.dat
Run Code Online (Sandbox Code Playgroud)

变成:

|parent
|    |a
|    |    123.dat
|    |b
|    |    123.dat
Run Code Online (Sandbox Code Playgroud)

我如何在 linux 中做到这一点?这个领域不是我的专长,到目前为止,我尝试寻找类似的东西没有成功。

Arr*_*cal 6

一种方法是在新位置创建新版本的父目录,然后跨子目录复制(如果它们包含 123.dat)。这使用 Bash shell 的 globbing 功能来查找子目录,因此只能在父目录下的目录上工作。在这个例子中,我假设 parent 位于一个名为的目录中/location1/,并将移动到/location2/

mkdir -p /location2/parent
for d in /location1/parent/*
  do if [[ -e "$d"/123.dat ]]; then
    cp -r "$d" /location2/parent
  done
fi
Run Code Online (Sandbox Code Playgroud)

作为 CLI one-liner,这将是:

mkdir -p /location2/parent; for d in /location1/parent/*; do if [[ -e "$d"/123.dat ]]; then cp -r "$d" /location2/parent; done; fi
Run Code Online (Sandbox Code Playgroud)

可以通过使用find使其更高效并添加多级子目录,或将源目录、目标目录和文件放入变量中来改进这一点。这应该做你现在需要的。

要复制包含特定文件的目录,但不复制任何其他文件,有一个不太优雅的解决方案,需要cding 进入源目录,最后的cd -部分将您返回到原始目录:

mkdir -p /location2/parent; cd /location1/parent/ && for d in ./*; do if [[ -e "$d"/123.dat ]]; then cp --parents "$d"/123.dat /location2/parent/; fi ; done; cd -
Run Code Online (Sandbox Code Playgroud)

作为多行:

mkdir -p /location2/parent
cd /location1/parent/ && for d in ./*
  do 
    if [[ -e "$d"/123.dat ]]; then 
      cp --parents "$d"/123.dat /location2/parent/
    fi
  done
cd -
Run Code Online (Sandbox Code Playgroud)


Jay*_*Jay 6

文件/文件夹结构:

$ find src | sort
src
src/a
src/a/123.dat
src/a/456.dat
src/b
src/b/123.dat
src/b/768.dat
src/c
src/c/456.dat
src/c/768.dat
Run Code Online (Sandbox Code Playgroud)

复制匹配的文件,保留相对路径(浅,不超过 1 个文件夹):

命令:

$ (cd src && cp -v --parents */123.dat ../dest)
Run Code Online (Sandbox Code Playgroud)

输出:

a -> ../dest/a
'a/123.dat' -> '../dest/a/123.dat'
b -> ../dest/b
'b/123.dat' -> '../dest/b/123.dat'
Run Code Online (Sandbox Code Playgroud)
  • 我用了一个子shell与()为了不使用时改变原来的工作目录cd。我不得不进入src执行前cp,以建立src/在底层目录dest
  • 不适用于高于 bash 参数限制的文件数(如果我没记错的话,通常在 65k 左右)

替代方法(使用find可调深度限制):

命令:

$ (cd src && find . -maxdepth 2  -type f -name '123.dat' -exec cp -v -t "../dest" --parents {} +)
Run Code Online (Sandbox Code Playgroud)

输出:

./b -> ../dest/./b
'./b/123.dat' -> '../dest/./b/123.dat'
./a -> ../dest/./a
'./a/123.dat' -> '../dest/./a/123.dat'
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 我用了一个子shell与()为了不使用时改变原来的工作目录cd。我不得不进入src执行前find,以建立src/在底层目录dest
  • 我指定-type f确保只123.dat考虑具有名称的文件,而不是碰巧具有该名称的目录

替代方法(使用rsync,没有深度限制):

命令:

$ rsync -rv --include=123.dat --include='*/' --exclude='*' --prune-empty-dirs src/ dest
Run Code Online (Sandbox Code Playgroud)

输出:

building file list ... done
created directory dest
./
a/
a/123.dat
b/
b/123.dat

sent 205 bytes  received 90 bytes  590.00 bytes/sec
total size is 0  speedup is 0.00
Run Code Online (Sandbox Code Playgroud)

再检查一遍:

$ find dest
dest
dest/b
dest/b/123.dat
dest/a
dest/a/123.dat
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 尾随/src/是故意的,因此,只有该文件夹的内容被复制,而不是文件夹本身。
  • --exclude='*' 默认情况下排除所有内容
  • --include='*/ 覆盖排除并包括所有文件夹
  • --include='123.dat' 覆盖排除并包含名称为“123.dat”的文件(和文件夹)
  • --prune-empty-dirs确保没有创建空文件夹(例如c