Copie arquivos de vários diretórios, se o nome deles aparecer em uma lista, para um único diretório

Copie arquivos de vários diretórios, se o nome deles aparecer em uma lista, para um único diretório

Eu tenho uma enorme árvore de pastas, cada uma com vários subdiretórios descendo em torno de 3 níveis. Aqui está um exemplo com apenas um nível:

$ tree
.
|-- AB.txt
|-- CD.txt
|-- destination_folder
|-- spreadsheet.txt
`-- subdirectory
    `-- EF.txt

2 directories, 4 files

Eu tenho uma lista de nomes de arquivos nos quais estou interessado, chamada spreadsheet.txt:

$ cat spreadsheet.txt 
AB.txt
CD.txt
EF.txt

Gostaria de copiar todos os arquivos que aparecem em spreadsheet.txtuma única pasta, por exemplo destination_folder. Qualquer ajuda recebida com muita gratidão! Eu imagino que isso envolverá finde cp, mas não consigo resolver isso,

Responder1

Executando finduma vez por nome de arquivo e assumindo que nenhum nome de arquivo contém novas linhas incorporadas:

#!/bin/sh

mkdir -p destination_folder || exit 1
while IFS= read -r name; do
    find . -path ./destination_folder -prune -o \
        -type f -name "$name" -exec cp {} destination_folder \;
done <spreadsheet.txt

Isso primeiro cria o diretório de destino no diretório atual (e termina se falhar). Em seguida, ele lê o arquivo de entrada, linha por linha, e chama findpara localizar qualquer arquivo normal que tenha esse nome. O diretório de destino é explicitamente evitado findquando -pruneo encontramos em nossa pesquisa.

Sempre que um arquivo com o nome correto for encontrado, ele será copiado para o diretório de destino. Se vários arquivos tiverem o mesmo nome, a cópia destination_folderserá substituída.

Se o diretório atual for enorme, ou se a lista de nomes de arquivos for longa (mas não muitos milhares de linhas), então esta será uma operação lenta. Podemos, portanto, optar por fazer umasolteiroligar para find. O código a seguir assume que ele é executado, por exemplo, bashpois usa arrays:

#!/bin/bash

mkdir -p destination_folder || exit 1

names=()
while IFS= read -r name; do
    names+=( -o -name "$name" )
done <spreadsheet.txt

find . -path ./destination_folder -prune -o \
    \( "${names[@]:1}" \) -type f -exec cp -t destination_folder {} +

Aqui, também optei por usar cp -t(uma cpextensão GNU) para poder chamar cpo menor número de vezes possível e não uma vez por arquivo encontrado.

O código acima constrói um array, namesque eventualmente estará no formato correto para uso com find. O comando que realmente é executado no final do código acima, dado o exemplo da sua pergunta, será

find . -path ./destination_folder -prune -o '(' -name AB.txt -o -name CD.txt -o -name EF.txt ')' -type f -exec cp -t destination_folder '{}' +

Para evitar o problema de colisões de nomes de arquivos no diretório de destino, se você estiver usando GNU cp(por exemplo, em um sistema Linux), use cpcom sua opção -bou --backup.

Em sistemas não-Linux, o GNU cppode frequentemente estar disponível gcpapós a instalação do GNU coreutils através de um gerenciador de pacotes.


Esse último script, mas para /bin/sh(sem arrays):

#!/bin/sh

mkdir -p destination_folder || exit 1

set --
while IFS= read -r name; do
    set -- -o -name "$name" "$@"
done <spreadsheet.txt

shift

find . -path ./destination_folder -prune -o \
    \( "$@" \) -type f -exec cp -t destination_folder {} +

Responder2

Um loop for simples:

for filename in $(cat spreadsheet.txt)
do
find . -name "$filename" -exec cp {} /destination/folder \;
done

informação relacionada