Cofnij akcję polecenia kopiowania (cp)

14

Skopiowałem jakiś plik z folderu źródłowego do folderu docelowego za pomocą poniższego wiersza poleceń w terminalu.

sudo cp From_SOURCE/* To_DESTINATION/

Teraz chcę cofnąć to polecenie.

αғsнιη
źródło
Nie jestem pewien, czy rozumiem. Po prostu usunąć kopię? (plik sudo rm)
Jacob Vlijm
@JacobVlijm to nie jest cofnięcie akcji, to osobne polecenie. Gdybym nie skopiował, mógłbym wcześniej użyć sugerowanego polecenia. I muszę coraz więcej czasu używać tego polecenia, aby usunąć ponad 1000 skopiowanych plików?
αғsнιη
@KasiyA - opcją byłoby znaleźć wszystkie wybrane nazwy plików From_SOURCE/*i usunąć je To_DESTINATION/; można to zrobić za pomocą bash - tutaj problemy mogą polegać na tym, że polecenie cp nadpisało wszystko, dowiązania symboliczne zostały skopiowane, pliki ze spacjami w nazwach itp. - więc mogło to mieć problemy i nieco zwariowane. Idealna opcja „cofnij” mogłaby uwzględniać wiele rzeczy.
Wilf,

Odpowiedzi:

13

Jeśli dobrze rozumiem, dzieje się tak:

  • Skopiowałeś (prawdopodobnie dużą) liczbę plików do istniejącego katalogu i potrzebujesz / chcesz cofnąć akcję.
  • Docelowy katalog zawiera wiele innych plików, które musisz tam przechowywać, co uniemożliwia po prostu usunięcie wszystkich plików z katalogu

Poniższy skrypt wyszukuje w oryginalnym katalogu (źródłowym) i wyświetla te pliki. Następnie przegląda katalog, do którego skopiowano pliki, i usuwa tylko wymienione pliki, ponieważ istnieją one w katalogu źródłowym.

tryElement jest dodawany, aby zapobiec błędom, na przykład w przypadku mogłeś usunięto niektóre pliki ręcznie już, lub jeśli nie wszystkie pliki z katalogu źródłowego zostały skopiowane do miejsca przeznaczenia. Jeśli potrzebujesz uprawnień sudo, po prostu uruchom skrypt z „sudo” (patrz poniżej).

Scenariusz

#!/usr/bin/env python

import os

source_dir = "/path/to/source" # the folder you copied the files from
target_folder = "/path/to/destination" # the folder you copied the files to

for root, dirs, files in os.walk(source_dir):
    for name in files:
        try:
            os.remove(target_folder+"/"+name)
        except FileNotFoundError:
            pass

Jak używać

  • Wklej skrypt do pustego pliku, zapisz go jako reverse.py,
  • Wstaw poprawne ścieżki do folderu źródłowego i docelowego,
  • Zrób to ze względów wygody,
  • Uruchom go za pomocą polecenia:

    [sudo] /path/to/reverse.py
    

Ostrzeżenie

Najpierw wypróbuj katalog testowy, jeśli dobrze rozumiem, co musisz osiągnąć!


Jeśli katalog jest „płaski”

W przypadku, gdy katalog źródłowy nie ma podkatalogów, skrypt może być jeszcze prostszy:

#!/usr/bin/env python

import os

source_dir = "/path/to/source" # the folder you copied the files from
target_folder = "/path/to/destination" # the folder you copied the files to

for file in os.listdir(source_dir):
    try:
        os.remove(target_folder+"/"+file)
    except FileNotFoundError:
        pass

Uwaga

Jeśli akcja kopia nadpisałeś (zastąpiony) plik o podobnej nazwie w okolicy, plik zostanie usunięty, ale oryginalny plik (oczywiście) nie może być sprowadzony przez skrypt. Zakłada się, że nie ma konfliktów nazw.

Jacob Vlijm
źródło
Działa, dziękuję. Prowadzę to sudo python3 reverse.py. Przyjmuję twoją odpowiedź.
αғsнιη
Doskonały! Dziękuję za miłe pytanie, które opisuje sytuację, w której prawdopodobnie wszyscy byliśmy :)
Jacob Vlijm
Uwaga: Jeśli jakikolwiek plik w źródle nadpisał plik w miejscu docelowym, ponieważ miał taką samą nazwę, nie można tego po prostu cofnąć , a ten skrypt po prostu go usunie. Zakłada się tutaj, że podczas inicjowania nie było żadnych konfliktów nazw cp.
thomasrutter
@neon_overload Prawidłowo, edytowałem to w mojej odpowiedzi.
Jacob Vlijm
To nie działa dla cp -r. Zmieniłem za os.removepomocą print, i wyprowadza kilka plików, które nie istnieją. Zakładam, że brakuje podkatalogów i tylko listy plików na końcu.
Typewar
7

TL; DR:

Wszystkie pliki, które są obecne w obu srci destmożna je usunąć w destnastępujący sposób:

find . -maxdepth 1 -type f -exec cmp -s '{}' "$destdir/{}" \; -exec mv -n "$destdir/{}" "$toDelete"/ \;

Aby uzyskać wyjaśnienie krok po kroku, patrz poniżej.

Uproszczenie problemu:

Aby zrozumieć, co właściwie zrobiliśmy polecenie, zaczynamy od uproszczenia:

Polecenie, które chcemy cofnąć, to

sudo cp From_SOURCE/* To_DESTINATION/

Dla zrozumienia, jak cofnąć, sudonie ma znaczenia.

Użyję nazw katalogów srcdla From_SOURCEi destdla To_DESTINATION.

Teraz naszym poleceniem jest:

cp src/* dest/

Jeśli srczawiera pliki f1, f2a f3, a katalogi d1i d2, powłoka rozszerza tę komendę:

cp src/f1 src/f2 src/f3 src/d1 src/d2 dest/

Bez opcji podoba -r, -Rczy -akomenda cpnie kopiuje katalogów.
Oznacza to, że polecenie je pominie, pokazując błąd dla każdego z nich:

$ cp src/f1 src/f2 src/f3 src/d1 src/d2 dest/
cp: omitting directory 'src/d1'
cp: omitting directory 'src/d2'

Oznacza to, że skopiowaliśmy tylko proste pliki, a nie katalogi dest.

Decydowanie, które pliki usunąć:

Możliwe, że były pliki desto tej samej nazwie co pliki src. W tym przypadku pliki zostały nadpisane (1). Przepraszam, jest już dla nich za późno. Odzyskaj je z najnowszej kopii zapasowej.

Jeśli chodzi o pliki, które tam są, chcemy usunąć tylko pliki, które zostały skopiowane. Pliki te istnieją w obu katalogach, o tej samej nazwie i tej samej zawartości.

Szukamy więc tych plików:

Po pierwsze, wchodzimy cdw to src, ponieważ findznacznie upraszcza następujące polecenia i ustawia zmienną na absolutną ścieżkę dest:

$ cd src
$ destdir="$(readlink -f dest)"

Następnie wyświetlamy wszystkie pliki w src:

$ find . -maxdepth 1 -type f

i dla każdego znalezionego pliku użyj, cmpaby sprawdzić, czy w dest jest plik o tej samej zawartości:

$ find . -maxdepth 1 -type f -exec cmp -s '{}' "$destdir/{}" \; -print

Usuwając pliki, ostrożnie:

Te pliki chcemy usunąć. Ale dla pewności najpierw przenosimy je do innego katalogu - i uruchamiamy polecenia przed ich uruchomieniem:

$ toDelete=/tmp/toDelete ; mkdir -p "$toDelete"
$ find . -maxdepth 1 -type f -exec cmp -s '{}' "$destdir/{}" \; -exec echo mv -n "$destdir/{}" "$toDelete"/ \;
mv -n /path/to/dest/./f1 /tmp/toDelete/`
mv -n /path/to/dest/./f2 /tmp/toDelete/`
mv -n /path/to/dest/./f3 /tmp/toDelete/`

Wygląda dobrze! Teraz możemy pominąć echouruchamianie prawdziwych mvpoleceń:

find . -maxdepth 1 -type f -exec cmp -s '{}' "$destdir/{}" \; -exec mv -n "$destdir/{}" "$toDelete"/ \;

Wszystkie pliki z desttego zostały skopiowane srci nadal są takie same w srci destsą teraz w /tmp/toDelete/i można je usunąć po ostatnim spojrzeniu.


Uwagi:
(1) Sprawdź, czy cpjest to alias do, czy coś cp -itakiego, aby zapobiec zastąpieniu plików, pytając najpierw.

Volker Siegel
źródło
5

To jest stare, ale chciałem tylko napisać czystą odpowiedź:

Najpierw przejdź do katalogu, w którym skopiowano pliki.

cd dest

Następnie lskatalog źródłowy i potokuj dane wyjściowerm

ls source | xargs rm

Michael Goldstein
źródło
2
parsowanie lsjest zazwyczaj złym pomysłem. mywiki.wooledge.org/ParsingLs Nie będę głosować za twoim postem, ale należy to zauważyć.
Sergiy Kolodyazhnyy 17.07.16
@SergiyKolodyazhnyy good point. Niemniej jednak: rm `ls`—dla pustego wcześniej folderu i gdy pliki mają nazwy bez spacji i znaków nowej linii.
Multifix