web-dev-qa-db-fra.com

Comment trouver et copier une liste arbitraire de fichiers dans le terminal?

J'essaie de trouver des fichiers dans le terminal pour le moment. J'ai trouvé de nombreuses réponses sur la façon de trouver des fichiers correspondant à un modèle spécifique, tels que des fichiers se terminant par une certaine extension, ou à partir d'une certaine période, etc.

Cependant, ce que j’ai essayé de comprendre, c’est comment trouver et copier plusieurs fichiers arbitraires, comme une liste de fichiers spécifiques.

Disons, par exemple, que j'ai les dossiers et fichiers suivants

/Documents/Folder_1/ avec contenu:

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

/Documents/Folder_2/ avec contenu:

file5.txt file6.txt file7.txt

/Documents/Folder_3/ avec contenu:

file8.txt, file9.txt

et ce que je veux faire est de créer un nouveau dossier appelé Folder_4 et de copier file2.txt, file5.txt et file9.txt dans ce nouveau dossier.

Est-il possible de le faire via un terminal?

Ce que j'ai essayé de faire est de faire une liste de texte des noms de fichiers et de l'appeler quelque chose comme: list.txt, par exemple.

file2.txt
file5.txt
file9.txt

et charger ce fichier texte dans la commande Rechercher, par exemple comme ceci:

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

en pensant que j'alimenterais ensuite la sortie avec une liste de textes ou une commande de copie, mais cela ne semble pas fonctionner.

Je me demande s’il existe un moyen de rechercher plusieurs fichiers arbitraires au moyen de la commande Rechercher, ou si j’y pense de la mauvaise façon?

EDIT: Je voulais juste dire merci pour les réponses et résumer la façon dont j'ai fini par aborder ce problème. J'apprécie tout particulièrement que quelqu'un m'ait expliqué que la commande find ne soit pas conçue pour accepter plus d'une entrée sans le drapeau -o, cela m'a fait me sentir un peu moins stupide de ne pas pouvoir le comprendre!

Ce que j'ai fini par faire était de créer un petit script Shell et de demander une entrée utilisateur et d'utiliser un tableau et une boucle for pour parcourir la liste.

En gros, le script avec lequel je me suis retrouvé ressemble plus ou moins à ceci:

#!/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
4
david

Malheureusement, le prédicat -name de la commande find n'accepte qu'un seul motif. Si vous souhaitez rechercher plusieurs fichiers par nom, vous devez les chaîner avec l'opérateur -o (logique OR) - quelque chose comme:

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

Cela rend difficile la construction de la recherche par programme à partir d’une liste; le plus proche, je peux venir à votre tentative de commande est:

  1. lire la liste dans un tableau Shell

    mapfile -t files < list.txt
    
  2. utiliser le printf du shell bash pour construire la liste de prédicats

    printf -- '-name "%s" -o ' "${files[@]}"
    
  3. utiliser eval pour évaluer la chaîne de commande résultante

Il y a une ride ici dans la mesure où si nous utilisons la fonctionnalité de réutilisation du format de printf pour construire la liste de cette façon, nous nous retrouvons avec un 'balançant' -o; nous pouvons contourner ce problème en terminant la liste par un test -false (puisque -o -false est un booléen non-op), de sorte que notre chaîne de prédicat finale devienne

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

Mettre tout cela ensemble - étant donné

$ 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

et

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

ensuite

$ 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

Pour copier des fichiers au lieu de simplement les lister, vous pouvez alors faire

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

résultant en

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

0 directories, 3 files

Remarque: la commande eval est puissante et potentiellement susceptible d’abus: utilisez-la avec précaution.


En pratique, étant donné que vous semblez vouloir ne trouver qu'un petit nombre de fichiers, l'approche KISS consiste à accepter le hit de performance de plusieurs appels find et à n'utiliser qu'une boucle. :

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

ou même en utilisant xargs

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

J'essaie toujours de comprendre votre question de la manière la plus claire.

Essayez-vous de trouver des fichiers spécifiques dans un répertoire? Si oui et qu'il existe un modèle que vous pouvez utiliser avec des expressions régulières pour les trouver, utilisez find <directory> -regex "your_regex" et s'il n'y en a pas, vous devez spécifier le nom du fichier que vous recherchez. Vous pouvez utiliser le python script ci-dessous pour copier vos fichiers.

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) 

Si ce qui précède n'est pas votre cas, je suggère d'utiliser ceci:

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

Le script bash ci-dessous, s'il est exécuté à partir du répertoire de niveau supérieur (par exemple, à partir de votre dossier ~/Documents), créera un nouveau répertoire et copiera/déplacera les fichiers souhaités. Par défaut, seuls les fichiers trouvés sont répercutés, ce qui permet à l'utilisateur de vérifier s'il s'agit du résultat souhaité. Remarque: remplacez echo par cp pour copier ou mv pour le déplacer.

La liste des fichiers est assignée à la variable regex avec le séparateur \| (ce qui pour grep signifie logique ou instruction)

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

Comme vous pouvez le voir ci-dessus, le script indique quel fichier a été trouvé et où il le copierait ou se déplacerait. Par exemple, à la dernière ligne, il copierait/déplacerait ./dir1/file1 vers ./dir4/file1 s'il utilisait réellement cp ou mv et pas seulement echo

0