Como encontro e copio uma lista arbitrária de arquivos no terminal?

3

Estou tentando encontrar arquivos no terminal no momento. Eu encontrei muitas respostas sobre como encontrar arquivos para um padrão específico, como arquivos que terminam em uma certa extensão, ou a partir de um determinado período de tempo, etc.

O que eu tenho tentado descobrir, no entanto, é como encontrar e copiar vários arquivos arbitrários, como uma lista de arquivos específicos.

Digamos, por exemplo, eu tenho as seguintes pastas e arquivos

/Documents/Folder_1/ com conteúdo:

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

/Documents/Folder_2/ com conteúdo:

file5.txt file6.txt file7.txt

/Documents/Folder_3/ com conteúdo:

file8.txt, file9.txt

e o que eu quero fazer é criar uma nova pasta chamada Folder_4 e copiar file2.txt , file5.txt e file9.txt para essa nova pasta.

Existe alguma maneira de fazer isso através do terminal?

O que eu tentei fazer foi criar uma lista de texto com os nomes dos arquivos e chamá-la de algo como list.txt, por exemplo

file2.txt
file5.txt
file9.txt

e carregando esse arquivo de texto no comando Localizar, por exemplo, assim:

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

com o pensamento de que eu iria então alimentar a saída para uma lista de texto ou um comando de cópia, mas isso não parece funcionar.

Eu estou querendo saber se existe alguma maneira de encontrar vários arquivos arbitrários através do comando Localizar, ou estou pensando sobre isso de maneira errada?

EDIT: Só queria dizer obrigado pelas respostas e resumir a maneira que acabei abordando esse problema. Eu particularmente aprecio que alguém me explicou que o comando find não foi projetado para aceitar mais de uma entrada sem o flag -o, isso me fez sentir um pouco menos estúpida por não ser capaz de descobrir isso!

O que acabei fazendo foi fazer um pequeno script de shell e pedir a entrada do usuário e usar um array e um loop for para percorrer a lista.

Basicamente, o script que acabei sendo mais ou menos assim:

#!/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
    
por david 15.10.2016 / 01:32

3 respostas

4

Infelizmente, o predicado find do comando -name aceita apenas um único padrão. Se você quiser pesquisar vários arquivos pelo nome, será necessário encadeá-los com o operador -o (logical OR ) - algo como:

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

Isso dificulta a construção da pesquisa programaticamente a partir de uma lista; o mais próximo que eu posso chegar do seu comando é:

  1. leia a lista em um array de shell

    mapfile -t files < list.txt
    
  2. use printf do shell bash para construir a lista de predicados

    printf -- '-name "%s" -o ' "${files[@]}"
    
  3. use eval para avaliar a sequência de comandos resultante

Há um problema aqui, se usarmos o recurso de reutilização de formato de printf para construir a lista dessa maneira, ficamos com um 'pendente' -o ; podemos contornar isso terminando a lista com um -false test (já que -o -false é um booleano não operacional) para que nossa string de predicado final se torne

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

Colocando tudo junto - dado

$ 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

e

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

então

$ 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

Para copiar arquivos em vez de apenas listá-los, você pode fazer

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

resultando em

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

0 directories, 3 files

Observação: o comando eval é poderoso e potencialmente aberto ao abuso: use com cuidado.

Na prática, considerando que você parece querer encontrar apenas um pequeno número de arquivos, a abordagem KISS seria para aceitar o hit de desempenho de várias chamadas de find e apenas usar um loop:

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

ou até mesmo usando xargs

xargs -a list.txt -n1 -I@ find dir/ -name @ -exec cp -v -- {} newdir/ \;
    
por steeldriver 15.10.2016 / 03:27
0

Ainda tentando descobrir sua pergunta da maneira mais clara.

Você está tentando encontrar alguns arquivos específicos em um diretório? Se sim e houver um padrão que você poderia usar expressões regulares para encontrá-las, use find <directory> -regex "your_regex" e, se não houver um padrão, especifique os nomes dos arquivos que você está procurando. Você pode usar o script python abaixo para copiar o seu arquivo. arquivos.

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) 

Se o acima não for o seu caso, sugiro usar isso:

find <somewhere> -name "something" | xargs cp -t <your_destination>
    
por Shadow_m2 15.10.2016 / 01:57
0

O script bash abaixo, se executado a partir do diretório de nível superior (digamos da pasta ~/Documents ), criará um novo diretório e copiará / moverá os arquivos desejados. Por padrão, ele apenas ecoa os arquivos encontrados, para que o usuário possa verificar se esse é o resultado desejado. Nota: substitua echo por cp para copiar ou mv para mover.

A lista de arquivos é atribuída à variável regex com \| separator (que para grep significa lógica ou declaração)

copy_files.sh :

#!/bin/bash
new_dir="./dir4"
[ -d "$new_dir" ] ||  mkdir "$new_dir"
regex="file1\|file3\|file6"
find . -print0 | while IFS= read -d $'
bash-4.3$ ./copy_files.sh 
Found file6
./dir3/file6 ./dir4/file6
Found file3
./dir1/file3 ./dir4/file3
Found file1
./dir1/file1 ./dir4/file1
' 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
#!/bin/bash
new_dir="./dir4"
[ -d "$new_dir" ] ||  mkdir "$new_dir"
regex="file1\|file3\|file6"
find . -print0 | while IFS= read -d $'
bash-4.3$ ./copy_files.sh 
Found file6
./dir3/file6 ./dir4/file6
Found file3
./dir1/file3 ./dir4/file3
Found file1
./dir1/file1 ./dir4/file1
' 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

Como você pode ver acima, o script relata qual arquivo foi encontrado e onde ele seria copiado ou movido, por exemplo, na última linha, ele copiaria / moveria ./dir1/file1 para ./dir4/file1 se isso estivesse realmente usando cp ou mv e não apenas echo

    
por Sergiy Kolodyazhnyy 15.10.2016 / 02:24