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.txt
uma única pasta, por exemplo destination_folder
. Qualquer ajuda recebida com muita gratidão! Eu imagino que isso envolverá find
e cp
, mas não consigo resolver isso,
Responder1
Executando find
uma 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 find
para localizar qualquer arquivo normal que tenha esse nome. O diretório de destino é explicitamente evitado find
quando -prune
o 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_folder
será 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, bash
pois 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 cp
extensão GNU) para poder chamar cp
o menor número de vezes possível e não uma vez por arquivo encontrado.
O código acima constrói um array, names
que 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 cp
com sua opção -b
ou --backup
.
Em sistemas não-Linux, o GNU cp
pode frequentemente estar disponível gcp
apó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