Kopiowanie wielu określonych plików z jednego folderu do drugiego

133

Mam duży folder zdjęć (tysiące) i mam długą listę plików, według dokładnej nazwy pliku, które muszę skopiować do innego folderu. Chcę wiedzieć, czy istnieje sposób na wybranie kilku konkretnych plików z tego folderu według nazwy i skopiowanie ich do innego folderu za pomocą terminala bez kopiowania ich osobno?

Ktoś ze zdecydowanie zbyt wieloma piktami
źródło
Fyi, do kopiowania określonych folderów użyj:, cp -rp /copying/from/{folder1/,folder2/,folder3/} path/to/foldergdzie pjest do kopiowania uprawnień do folderu.
Raktim Biswas

Odpowiedzi:

179

Wystarczy skopiować wiele plików jednocześnie z wiersza polecenia

Można to osiągnąć na kilka sposobów. Najłatwiejsze, jakie widziałem, to skorzystanie z poniższych.

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

Składnia używa polecenia cp, a następnie ścieżki do katalogu, w którym znajdują się żądane pliki, a wszystkie pliki, które chcesz skopiować, są owinięte w nawiasy i oddzielone przecinkami.

Pamiętaj, że między plikami nie ma spacji. Ostatnia część polecenia, /home/usr/destination/to katalog, do którego chcesz skopiować pliki.

lub jeśli wszystkie pliki mają ten sam prefiks, ale różne zakończenia, możesz zrobić coś takiego:

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

Gdzie plik 1, plik 2, plik 3 i plik 4 zostaną skopiowane.

Z tego, jak sformułowałeś pytanie, uważam, że tego właśnie szukasz, ale brzmi to również tak, jakbyś szukał polecenia do odczytania z listy plików i skopiowania ich wszystkich do określonego katalogu. W takim przypadku daj mi znać, a ja zmienię odpowiedź.

Radzenie sobie z duplikatami za pomocą Pythona

Napisałem więc mały skrypt Pythona, który moim zdaniem powinien wykonać zadanie. Nie jestem jednak pewien, jak dobrze znasz język Python (jeśli w ogóle), więc postaram się wyjaśnić, jak najlepiej korzystać z tego skryptu i zadaj tyle pytań, ile potrzebujesz.

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))

Ten skrypt powinien być stosunkowo prosty w użyciu. Najpierw skopiuj powyższy kod do programu gedit (powinien być preinstalowany w Ubuntu) lub w dowolnym innym edytorze tekstu.

Po zakończeniu zapisz plik jako move.py w swoim katalogu domowym (może to być dowolny katalog, ale dla ułatwienia instrukcji, po prostu skorzystaj z katalogu domowego) lub dodaj katalog, w którym znajduje się plik do ŚCIEŻKI. Następnie cdprzejdź do katalogu domowego (lub innego katalogu, w którym zapisałeś move.py in) z terminala i wpisz następującą komendę:

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

Powinno to skopiować wszystkie pliki z katalogu źródłowego do katalogu docelowego z duplikatami w formacie pic (1) .jpg, pic (2) .jpg i tak dalej. plik.txt powinien być plikiem, który zawiera listę wszystkich zdjęć, które chcesz skopiować, z każdym wpisem w osobnym wierszu.

Skrypt ten w żaden sposób nie powinien wpływać na katalog źródłowy, jednak po prostu upewnij się, że wprowadzasz prawidłowe ścieżki do katalogu źródłowego i docelowego, a najgorsze, co może się zdarzyć, to skopiowanie plików do niewłaściwego katalogu.

Notatki

  • Ten skrypt zakłada, że ​​wszystkie oryginalne obrazy znajdują się w tym samym katalogu. Jeśli chcesz, aby sprawdził także podkatalogi, skrypt będzie musiał zostać zmodyfikowany.
  • Jeśli przez pomyłkę wpiszesz nazwę pliku, skrypt wypluje błąd
    „plik nie istnieje” i wyświetli monit o naciśnięcie „enter”, aby kontynuować, a skrypt będzie kontynuował kopiowanie reszty listy.
  • Nie zapomnij o końcowej /ścieżce zarówno do
    katalogu źródłowego, jak i ścieżki do katalogu docelowego. W przeciwnym razie skrypt wypluje ci błąd.
Bryan
źródło
Dziękuję, że rozwiązałem większość mojego problemu, jednak niektóre pliki pojawiają się na liście dwa lub więcej razy. Jak mogę automatycznie zmieniać nazwy tych duplikatów? (Tak, potrzebuję duplikatów do tego, co robię.)
Ktoś ze zdecydowanie zbyt dużą liczbą pict
Czy duplikaty z różnych podfolderów czy oznaczone są czymś w rodzaju file.jpg i file (1) .jpg? w przeciwnym razie nie jestem pewien, jak masz identyczne pliki. Aby nieco odpowiedzieć na twoje pytanie, nie sądzę, że będziesz w stanie znaleźć prostą linijkę, która automatycznie zmieni nazwę duplikatów. Myślę, że bash lub inny rodzaj skryptu musiałby zostać napisany, aby uzyskać ten rodzaj funkcjonalności. Widziałem jednak kilka niedorzecznych poleceń w jednym wierszu, więc może jakiś kreator linuksa może się pojawić i udzielić odpowiedzi.
Bryan
Na początku mam tylko jeden plik, jednak nazwy plików znajdują się na liście dwa lub więcej razy. Mniej więcej to, co muszę zrobić, gdy próbuję umieścić plik w nowym folderze, a plik o tej nazwie już istnieje, muszę dodać (2) na końcu nazwy pliku i umieść to.
Ktoś ze zdecydowanie zbyt dużą liczbą pict
Chciałbym tylko zasugerować podkreślenie części o braku spacji na liście plików do skopiowania. Jeśli są jakieś spacje, nawet między plikami {foo, bar, ..} polecenie nie będzie działać.
franksands,
Dla tych, którzy szukają dalszych skrótów, możesz skorzystać z podstawiania poleceń, aby zastąpić bezwzględną ścieżkę plików, tj. cp $(pwd)/{file1,file2} /path/to/dst/Lub cp $(pwd)/{file1,file2} $(pwd)/{renamed1,renamed2}, itd.
baelx
49

Być może brakuje mi szczegółów twojego pytania, ale udzielone odpowiedzi wydają się przesadne. Jeśli chcesz rozwiązania wiersza polecenia, a nie skryptu, dlaczego nie:

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

Zaletą robienia tego w ten sposób jest to, że możesz uzupełniać nazwy plików tabulatorami

Ross Allen
źródło
5
Zwraca to „nielegalną opcję --t” w systemie macOS Sierra
geoyws,
Świetna odpowiedź, o wiele prostsza. @geoyws Może to POSIX lub GNU, czego nie ma wersja Apple. Działa dobrze dla mnie w systemie Debian Linux lub Windows z MSYS2.
underscore_d
fajna sugestia! pracował w arch. linuksie.
Edu Ruiz,
brew install coreutilswtedy możesz dostać zwykłe rzeczy GNU z gprefiksem. Tak to będziegcp -t ....
T.Chmelevskij
Mac OS używa wersji BSD cp, aw szczególności ani OpenBSD, ani FreeBSD nie mają tej opcji, więc tak - jest to cp specyficzne dla GNU . Nie jest to również określone przez standard POSIX . Ponieważ jest to strona specyficzna dla Ubuntu, jest to dopuszczalne, ale do przenoszenia skryptów między systemami operacyjnymi lepiej jest trzymać się standardu POSIX
Sergiy Kolodyazhnyy
7

Oto czyste rozwiązanie bash. Odczyta nazwy plików z pliku wejściowego (jeden na linię) i skopiuje każdy z nich, zmieniając nazwę duplikatów.

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

Zapisz ten skrypt w folderze w swoim $PATHi wywołaj go z listą ścieżek jako danych wejściowych:

auto_copy.sh < file_paths.txt

Możesz również uruchomić całą rzecz jako polecenie z terminala:

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;
terdon
źródło
1

Zgodnie z opisem pytania rozumiem, że:

  • istnieje lista plików, prawdopodobnie plik tekstowy input.txt
  • lista zawiera tylko nazwy plików
  • istnieje konkretny katalog, w którym znajdują się te nazwy plików.

W związku z tym można użyć następującego polecenia:

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

Wyjaśnienie:

  • -I % określa symbol aktualnie przetwarzanego pliku do użycia w poleceniu
  • --arg-file=input.txt określa pobieranie argumentów z polecenia input.txt
  • cp /path/to/origin_dir/% /path/to/destination/wykona cppolecenie z /path/to/origin_dir/%zamienionym na /path/to/origin_dir/i nazwa aktualnie przetwarzanego pliku.

Praktyczny przykład:

$ 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
Sergiy Kolodyazhnyy
źródło