如何在终端中查找并复制任意文件列表?

如何在终端中查找并复制任意文件列表?

我现在正试图弄清楚如何在终端中查找文件。我找到了很多关于如何查找特定模式的文件的答案,例如以某个扩展名结尾的文件或来自某个时间范围的文件等。

不过,我一直在试图弄清楚如何查找和复制多个任意文件,比如特定文件的列表。

例如,我有以下文件夹和文件

/Documents/Folder_1/内容:

file1.txt file2.txt file3.txt file4.txt

/Documents/Folder_2/内容:

file5.txt file6.txt file7.txt

/Documents/Folder_3/内容:

file8.txt, file9.txt

我想要做的是创建一个名为的新文件夹,然后将Folder_4其复制到该新文件夹。file2.txtfile5.txtfile9.txt

有没有办法通过终端做到这一点?

我尝试做的是制作一个文件名的文本列表,并将其命名为 list.txt,例如

file2.txt
file5.txt
file9.txt

并将该文本文件加载到“查找”命令中,例如像这样:

find /home/usr/Documents -name $cat list.txt

我原本想将输出提供给文本列表或复制命令,但这似乎不起作用。

我想知道是否有任何方法可以通过“查找”命令查找多个任意文件,或者我是否以错误的方式考虑这个问题?

编辑:只是想感谢大家的回答,并总结一下我最终解决这个问题的方法。我特别感谢有人向我解释说,如果没有 -o 标志,find 命令就不能接受多个输入,这让我觉得自己没那么愚蠢,因为我无法弄清楚!

我最终做的是编写一个小 shell 脚本并要求用户输入并使用数组和 for 循环遍历列表。

基本上我最终得到的脚本大致如下:

#!/bin/bash

#take user input
echo "Please enter list of filenames to search"
read list
echo "Please enter the folder path to search"
read path
echo "Please enter the destination folder path"
read destination

#use inputted list as input for array, and then use a for loop to find each item on list one by one
array=($list)
for i in "${array[@]}"
do
    find "$path" -iname "i" -exec cp '{}' "$destination" \;
done

答案1

不幸的是,该find命令的-name谓词仅接受单个模式。如果您想按名称搜索多个文件,则需要使用-o(逻辑OR)运算符将它们链接起来 - 类似于:

find Documents/ \( -name "file2.txt" -o -name "file5.txt" -o -name "file9.txt" \) -print

这使得从列表以编程方式构建搜索变得很棘手;我能想到的最接近您尝试的命令是:

  1. 将列表读入 shell 数组

    mapfile -t files < list.txt
    
  2. 使用 bash shellprintf构建谓词列表

    printf -- '-name "%s" -o ' "${files[@]}"
    
  3. 用于eval评估结果命令字符串

这里有一个问题,因为如果我们使用 的printf格式重用功能以这种方式构造列表,我们就会留下一个“悬空” -o;我们可以通过使用测试来终止列表来解决这个问题-false(因为-o -false是一个布尔无操作),这样我们的最终谓词字符串就变成了

"\( $(printf -- '-name "%s" -o ' "${files[@]}") -false \)"

综合起来——

$ tree dir
dir
├── Folder_1
│   ├── file1.txt
│   ├── file2.txt
│   ├── file3.txt
│   └── file4.txt
├── Folder_2
│   ├── file5.txt
│   ├── file6.txt
│   └── file7.txt
└── Folder_3
    ├── file8.txt
    └── file9.txt

3 directories, 9 files

$ cat list.txt
file2.txt
file5.txt
file9.txt

然后

$ mapfile -t files < list.txt

$ eval find dir/ "\( $(printf -- '-name "%s" -o ' "${files[@]}") -false \)" -print
dir/Folder_1/file2.txt
dir/Folder_2/file5.txt
dir/Folder_3/file9.txt

要复制文件而不是仅仅列出它们,你可以这样做

$ mkdir newdir
$ eval find dir/ "\( $(printf -- '-name "%s" -o ' "${files[@]}") -false \)" -exec cp -t newdir/ -- {} +

导致

$ tree newdir
newdir
├── file2.txt
├── file5.txt
└── file9.txt

0 directories, 3 files

注意: eval 命令功能强大,但可能被滥用:请谨慎使用。


实际上,考虑到你似乎只想查找少量文件,方法是接受多次find调用的性能损失并只使用循环:

while read -r f; do
  find dir/ -name "$f" -exec cp -v -- {} newdir/ \;
done < list.txt

甚至使用xargs

xargs -a list.txt -n1 -I@ find dir/ -name @ -exec cp -v -- {} newdir/ \;

答案2

仍在尝试以最清晰的方式弄清楚您的问题。

您是否尝试在目录中查找某些特定文件?如果是,并且存在可以使用正则表达式找到它们的模式,则使用find <directory> -regex "your_regex" ;如果没有模式,则需要指定要查找的文件名。您可以使用下面的 python 脚本复制文件。

import shutil
import os

list = [file1.txt, file2.txt] # replace with your file list.
destination = "your_destination" # replace with your destination.
directory = os.getcwd() # replace os.getcwd() with your directory.

for dirpath, dirname, filenames in os.walk(directory, topdown='true'):
    for file in filenames :
        if file in list :
            shutil.copy(os.path.join(dirpath, file), destination) 

如果上述情况不是你的情况那么我建议使用这个:

find <somewhere> -name "something" | xargs cp -t <your_destination>

答案3

如果从顶层目录(例如从您的~/Documents文件夹)执行以下 bash 脚本,它将创建新目录并复制/移动您想要的文件。默认情况下,它仅回显找到的文件,以便用户可以检查这是否是他们想要的结果。注意:echocp复制或mv 移动替换。

文件列表被分配给regex带分隔符的变量\|(表示grep逻辑或语句)

copy_files.sh

#!/bin/bash
new_dir="./dir4"
[ -d "$new_dir" ] ||  mkdir "$new_dir"
regex="file1\|file3\|file6"
find . -print0 | while IFS= read -d $'\0' line;
do
    if grep -q "$regex" <<< $line
    then
       filename=$(basename "$line")
       printf "%s\n" "Found $filename"
       echo "$line" "$new_dir"/"$filename"
       # Replace echo with cp for copying,
       # or mv for moving
    fi   
done
bash-4.3$ ./copy_files.sh 
Found file6
./dir3/file6 ./dir4/file6
Found file3
./dir1/file3 ./dir4/file3
Found file1
./dir1/file1 ./dir4/file1

正如您在上面看到的,脚本报告了找到了什么文件以及它将把它复制或移动到哪里,例如在最后一行它将复制/移动./dir1/file1./dir4/file1如果这实际上是在使用cpmv而不仅仅是echo

相关内容