Copia di più file specifici da una cartella a un'altra


133

Ho una grande cartella di immagini (migliaia) e ho un lungo elenco di file, con il nome esatto del file, che devo copiare in un'altra cartella. Voglio sapere se esiste un modo per selezionare diversi file specifici da questa cartella, per nome, e copiarli in un'altra cartella, usando il terminale, senza copiarli singolarmente?


Cordiali saluti, per copiare cartelle specifiche usare:, cp -rp /copying/from/{folder1/,folder2/,folder3/} path/to/folderdove pserve per copiare i permessi delle cartelle.
Raktim Biswas,

Risposte:


179

Copia semplicemente più file contemporaneamente dalla riga di comando

Esistono diversi modi per raggiungere questo obiettivo. Il più semplice che abbia mai visto è usare quanto segue.

cp /home/usr/dir/{file1,file2,file3,file4} /home/usr/destination/

La sintassi utilizza il comando cp seguito dal percorso della directory in cui si trovano i file desiderati con tutti i file che si desidera copiare racchiusi tra parentesi e separati da virgole.

Assicurati di notare che non ci sono spazi tra i file. L'ultima parte del comando, /home/usr/destination/è la directory in cui si desidera copiare i file.

o se tutti i file hanno lo stesso prefisso ma finali diversi, potresti fare qualcosa del genere:

cp /home/usr/dir/file{1..4} ./

Dove verranno copiati file1, file2, file3 e file4.

Da come hai formulato la domanda, credo che questo sia quello che stai cercando, ma sembra anche che tu stia cercando un comando per leggere da un elenco di file e copiarli tutti in una determinata directory. In tal caso, fammi sapere e modificherò la mia risposta.

Gestire i duplicati con Python

Quindi ho scritto un piccolo script in Python che credo dovrebbe portare a termine il lavoro. Tuttavia, non sono sicuro di quanto tu sia esperto di Python (se versato), quindi proverò a spiegare come usare questo script il meglio che posso e per favore fai tutte le domande su di esso di cui hai bisogno.

import os,sys,shutil
### copies a list of files from source. handles duplicates.
def rename(file_name, dst, num=1):
    #splits file name to add number distinction
    (file_prefix, exstension) = os.path.splitext(file_name)
    renamed = "%s(%d)%s" % (file_prefix,num,exstension)

    #checks if renamed file exists. Renames file if it does exist.
    if os.path.exists(dst + renamed):
        return rename(file_name, dst, num + 1)
    else:
        return renamed

def copy_files(src,dst,file_list):
    for files in file_list:
        src_file_path = src + files
        dst_file_path = dst + files
        if os.path.exists(dst_file_path):
            new_file_name =  rename(files, dst)
            dst_file_path = dst + new_file_name

        print "Copying: " + dst_file_path
        try:
            shutil.copyfile(src_file_path,dst_file_path)
        except IOError:
            print src_file_path + " does not exist"
            raw_input("Please, press enter to continue.")

def read_file(file_name):
    f = open(file_name)
    #reads each line of file (f), strips out extra whitespace and 
    #returns list with each line of the file being an element of the list
    content = [x.strip() for x in f.readlines()]
    f.close()
    return content

src = sys.argv[1]
dst = sys.argv[2]
file_with_list = sys.argv[3]

copy_files(src,dst,read_file(file_with_list))

Questo script dovrebbe essere relativamente semplice da usare. Prima di tutto, copia il codice sopra nel programma gedit (dovrebbe essere preinstallato in Ubuntu) o in qualsiasi altro editor di testo.

Al termine, salva il file come move.py nella tua home directory (può essere una qualsiasi directory ma per semplicità di istruzioni, usa semplicemente la home directory) o aggiungi la directory in cui è contenuto il file nel tuo PERCORSO. Quindi cdnella tua home directory (o in qualunque altra directory in cui hai salvato move.py) dal terminale e digita il seguente comando:

python move.py /path/to/src/ /path/to/dst/ file.txt

Questo dovrebbe copiare tutti i file elencati dalla directory di origine alla directory di destinazione con i duplicati che assumono il formato pic (1) .jpg, pic (2) .jpg e così via. file.txt dovrebbe essere un file che elenca tutte le immagini che si desidera copiare con ciascuna voce su una riga separata.

Questo script non dovrebbe in alcun modo influire sulla directory di origine, tuttavia assicurati di inserire i percorsi corretti per la directory di origine e di destinazione e il peggio che potrebbe accadere è copiare i file nella directory sbagliata.

Appunti

  • Questo script presuppone che tutte le immagini originali siano nella stessa directory. Se vuoi che controlli anche le sottodirectory, lo script dovrà essere modificato.
  • Se si digita inavvertitamente un nome file, lo script emetterà l'errore
    "file non esiste" e verrà richiesto di "premere Invio" per continuare e lo script continuerà a copiare il resto dell'elenco.
  • Non dimenticare il trailing /sia sul percorso della
    directory di origine che sul percorso della directory di destinazione. Altrimenti lo script ti restituirà un errore.

Grazie, che ha risolto la maggior parte del mio problema, tuttavia, alcuni file compaiono nell'elenco due o più volte, come posso fare per rinominare automaticamente questi duplicati? (Sì, ho bisogno dei duplicati per quello che sto facendo.)
Qualcuno con troppe foto

I duplicati provengono da diverse sottocartelle o sono etichettati come file.jpg e file (1) .jpg? altrimenti non sono sicuro di come i tuoi file siano identici. Per rispondere in qualche modo alla tua domanda, non credo che sarai in grado di trovare una linea semplice che rinominerà automaticamente i duplicati per te. Penso che un bash o qualche altro tipo di script dovrebbero essere scritti per ottenere quel tipo di funzionalità. Tuttavia, ho visto alcuni comandi abbastanza ridicoli su una riga, quindi forse un mago di Linux potrebbe apparire e fornire la risposta.
Bryan

Ho solo uno di ogni file in origine, tuttavia i nomi dei file sono nell'elenco due o più volte. Più o meno ciò di cui ho bisogno per farlo, quando provo a posizionare un file nella nuova cartella e un file con quel nome esiste già, ho bisogno che aggiunga un (2) circa alla fine del nome del file e posizionalo.
Qualcuno con troppe foto

Voglio solo suggerire di sottolineare la parte relativa alla mancanza di spazi nell'elenco dei file da copiare. Se ci sono spazi, anche tra i file {foo, bar, ..} il comando non funzionerà.
franksands

Per chi cerca ulteriori scorciatoie, è possibile utilizzare la sostituzione dei comandi per sostituire il percorso assoluto dei file, ovvero cp $(pwd)/{file1,file2} /path/to/dst/o cp $(pwd)/{file1,file2} $(pwd)/{renamed1,renamed2}, ecc.
baelx

50

Forse mi manca un dettaglio della tua domanda, ma le risposte fornite sembrano eccessive. Se vuoi una soluzione a riga di comando e non uno script, perché no:

cd /path/to/src/
cp -t /path/to/dst/ file1 file2 file3 ...

La cosa bella di farlo in questo modo è che puoi completare i nomi dei file


5
Questo restituisce una "opzione illegale --t" in macOS Sierra
geoyws,

Ottima risposta, molto più semplice. @geoyws Forse è una cosa POSIX o GNU che la versione di Apple non ha. Funziona bene per me su Debian Linux o Windows con MSYS2.
underscore_d

bel suggerimento! ha funzionato in arch linux.
Edu Ruiz,

brew install coreutilsallora puoi ottenere le solite cose gnu con gprefisso. Quindi saràgcp -t ....
T.Chmelevskij il

Mac OS usa la versione BSD di cp, e in particolare né OpenBSDFreeBSD hanno questa opzione, quindi sì - questo è GNU cp specifico. Inoltre, non è specificato dallo standard POSIX . Poiché si tratta di un sito specifico di Ubuntu, questo è accettabile, ma per il porting degli script tra i sistemi operativi è meglio attenersi allo standard POSIX
Sergiy Kolodyazhnyy,

7

Ecco una soluzione bash pura. Leggerà i nomi dei file da un file di input (uno per riga) e li copierà, rinominando i duplicati.

#!/usr/bin/env bash

## The destination folder where your files will
## be copied to.
dest="bar";

## For each file path in your input file
while read path; do 
    ## $target is the name of the file, removing the path. 
    ## For example, given /foo/bar.txt, the $target will be bar.txt.
    target=$(basename "$path"); 
    ## Counter for duplicate files
    c=""; 
    ## Since $c is empty, this will check if the
    ## file exists in target.
    while [[ -e "$dest"/"$target"$c ]]; do
        echo "$target exists"; 
        ## If the target exists, add 1 to the value of $c
        ## and check if a file called $target$c (for example, bar.txt1)
        ## exists. This loop will continue until $c has a value
        ## such that there is no file called $target$c in the directory.
        let c++; 
        target="$target"$c; 
    done; 
    ## We now have everything we need, so lets copy.
    cp "$path" "$dest"/"$target"; 
done

Salva questo script in una cartella nella tua $PATHe chiamalo con l'elenco dei percorsi come input:

auto_copy.sh < file_paths.txt

Puoi anche eseguire l'intera cosa come comando dal terminale:

while read path; do 
   target=$(basename "$path"); 
   c=""; 
   while [[ -e bar/"$target"$c ]]; do 
    echo "$target exists"; 
    let c++; 
    target="$target"$c; 
   done; 
   cp "$file" bar/"$target"; 
done < file_names;

1

Come da descrizione della domanda, la mia comprensione è che:

  • c'è un elenco di file, presumibilmente un file di testo input.txt
  • l'elenco contiene solo nomi di file
  • c'è una directory particolare in cui si trovano questi nomi di file.

Pertanto, è possibile utilizzare il seguente comando:

xargs -I % --arg-file=input.txt cp  /path/to/origin_dir/%  /path/to/destination

Spiegazione:

  • -I % specifica il simbolo per il file attualmente elaborato da utilizzare all'interno del comando
  • --arg-file=input.txt specifica di prendere argomenti da cui comandare input.txt
  • cp /path/to/origin_dir/% /path/to/destination/eseguirà il cpcomando con la /path/to/origin_dir/%sostituzione con /path/to/origin_dir/e il nome del file attualmente elaborato.

Esempio pratico:

$ cat input.txt
file2.txt
file1.txt
file3.txt
$ ls ./docs
file1.txt  file2.txt  file3.txt
$ xargs -I % --arg-file=input.txt cp ./docs/% ./docs_destination/
$ ls ./docs_destination/
file1.txt  file2.txt  file3.txt
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.