Hoàn tác hành động sao chép lệnh (cp)


14

Tôi đã sao chép một số tệp từ thư mục nguồn vào thư mục đích bằng cách sử dụng dòng lệnh dưới đây trong thiết bị đầu cuối.

sudo cp From_SOURCE/* To_DESTINATION/

Bây giờ tôi muốn hoàn tác lệnh này.


Tôi không chắc tôi hiểu. Đơn giản chỉ cần loại bỏ các bản sao? (tập tin sudo rm)
Jacob Vlijm

@JacobVlijm không hoàn tác nó là một lệnh riêng. Nếu tôi không sao chép để tôi có thể sử dụng lệnh được đề xuất của bạn trước đó. Và tôi phải ngày càng sử dụng lệnh này để xóa hơn 1000 tệp được sao chép?
αғsнιη

@KasiyA - một tùy chọn sẽ là tìm tất cả các tên tệp được chọn từ đó From_SOURCE/*và xóa chúng khỏi To_DESTINATION/; điều này có thể được thực hiện với bash - các vấn đề ở đây có thể bao gồm lệnh cp ghi đè lên bất cứ thứ gì, liên kết tượng trưng được sao chép, các tệp có khoảng trắng trong tên, v.v. - vì vậy nó có thể có vấn đề và hơi khó hiểu. Một tùy chọn 'hoàn tác' hoàn hảo có thể phải tính đến rất nhiều thứ.
Wilf

Câu trả lời:


13

Nếu tôi hiểu rõ, đây là trường hợp:

  • Bạn đã sao chép một số lượng tệp (có lẽ lớn) vào một thư mục hiện có và bạn cần / muốn đảo ngược hành động.
  • Thư mục được nhắm mục tiêu chứa một số tệp khác mà bạn cần giữ ở đó, điều này khiến bạn không thể xóa tất cả các tệp khỏi thư mục

Kịch bản bên dưới nhìn vào thư mục gốc (nguồn) và liệt kê các tệp đó. Sau đó, nó nhìn vào thư mục bạn đã sao chép các tệp vào và chỉ xóa các tệp được liệt kê khi chúng tồn tại trong thư mục nguồn.

Phần trytử được thêm vào để ngăn lỗi, ví dụ trong trường hợp bạn có thể đã xóa một số tệp theo cách thủ công hoặc nếu không phải tất cả các tệp từ thư mục nguồn đã được sao chép đến đích. Nếu bạn cần đặc quyền sudo, chỉ cần chạy tập lệnh với "sudo" (xem bên dưới).

Kịch bản

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

Cách sử dụng

  • Dán tập lệnh vào một tập tin trống, lưu nó dưới dạng reverse.py,
  • Chèn các đường dẫn chính xác cho thư mục nguồn và đích,
  • Làm cho nó thực thi được vì lý do thuận tiện,
  • Chạy nó bằng lệnh:

    [sudo] /path/to/reverse.py
    

Cảnh báo

Trước tiên hãy thử một thư mục kiểm tra nếu tôi hiểu rõ những gì bạn cần phải đạt được!


Nếu sourcedirectory là "phẳng"

Trong trường hợp thư mục nguồn không có thư mục con, tập lệnh thậm chí có thể đơn giản hơn:

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

Ghi chú

Nếu hành động sao chép ghi đè (thay thế) một tệp có tên tương tự ở đích, tệp sẽ bị xóa, nhưng tệp gốc sẽ (tất nhiên) sẽ không được tập lệnh quay lại. Giả định là không có xung đột tên.


Nó hoạt động, cảm ơn bạn. Tôi chạy nó bằng cách sudo python3 reverse.py. Tôi chấp nhận câu trả lời của bạn.
1

Hoàn hảo! Cảm ơn bạn vì một câu hỏi hay, trong đó mô tả một tình huống có lẽ tất cả chúng ta đã gặp phải :)
Jacob Vlijm

Lưu ý: Nếu bất kỳ tệp nào trong nguồn ghi đè lên một tệp ở đích vì nó có cùng tên, thì bạn không thể đơn giản hủy thực hiện và tập lệnh này sẽ xóa nó. Giả định ở đây là không có xung đột tên khi bạn thực hiện ban đầu cp.
thomasrutter

@neon_overload Đúng, chỉnh sửa nó trong câu trả lời của tôi.
Jacob Vlijm

Điều này không làm việc cho cp -r. Tôi đã thay đổi os.removevới print, và nó xuất ra một loạt các file đó không tồn tại. Tôi cho rằng nó thiếu các thư mục con và chỉ liệt kê các tệp ở cuối.
Máy đánh chữ

7

TL; DR:

Tất cả các tệp có trong cả hai srcdestcó thể được xóa khỏi destnhư thế này:

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

Để biết từng bước, giải thích, xem bên dưới.

Đơn giản hóa vấn đề:

Để hiểu lệnh mà chúng ta muốn hoàn tác thực sự đã làm gì, chúng ta bắt đầu bằng cách mô phỏng nó:

Lệnh chúng tôi muốn hoàn tác là

sudo cp From_SOURCE/* To_DESTINATION/

Để hiểu cách hoàn tác, sudo không liên quan.

Tôi sẽ sử dụng tên thư srcmục cho From_SOURCEdestcho To_DESTINATION.

Bây giờ, lệnh của chúng tôi là:

cp src/* dest/

Nếu srcchứa các tệp f1, f2f3, và các thư mục d1d2, shell sẽ mở rộng lệnh đó thành:

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

Không có các tùy chọn như -r, -Rhoặc -a, lệnh cpkhông sao chép các thư mục.
Điều đó có nghĩa là, lệnh sẽ loại bỏ chúng, hiển thị một lỗi cho mỗi trong số chúng:

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

Điều đó có nghĩa là, chúng tôi chỉ sao chép các tệp đơn giản và không có thư mục nào dest.

Quyết định loại bỏ các tập tin:

Có thể có các tệp destcó cùng tên với các tệp trongsrc . Trong trường hợp này, các tập tin đã bị ghi đè (1). Xin lỗi, quá muộn cho họ. Nhận chúng trở lại từ bản sao lưu mới nhất.

Về các tệp đang ở đó, chúng tôi muốn xóa chỉ các tệp đã được sao chép. Các tệp này tồn tại trong cả hai thư mục, có cùng tên và cùng một nội dung.

Vì vậy, chúng tôi tìm kiếm các tập tin này:

Đầu tiên, chúng ta cdvào src, bởi vì nó làm cho các findlệnh sau đơn giản hơn nhiều và đặt một biến thành đường dẫn tuyệt đối của mệnh:

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

Sau đó, chúng tôi liệt kê tất cả các tệp trong src:

$ find . -maxdepth 1 -type f

và, đối với mỗi tệp được tìm thấy, hãy sử dụng cmpđể kiểm tra xem có tệp nào có cùng nội dung trong mệnh hay không:

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

Xóa các tập tin, cẩn thận:

Những tập tin này là những cái chúng tôi muốn loại bỏ. Nhưng để chắc chắn, trước tiên chúng ta di chuyển chúng vào một thư mục khác - và xem qua các lệnh trước khi chạy chúng:

$ 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/`

Có vẻ tốt! Bây giờ chúng ta có thể bỏ qua echođể chạy các mvlệnh thực sự :

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

Tất cả các tệp từ destđó đã được sao chép từ srcvà vẫn thực sự giống nhau srcdesthiện đang ở trong /tmp/toDelete/và có thể được xóa sau khi xem qua.


Lưu ý:
(1) Kiểm tra xem có phải cplà bí danh cp -ihay không, điều đó có ngăn được các tập tin ghi đè bằng cách hỏi trước không.


5

Điều này đã cũ, nhưng tôi chỉ muốn đăng một câu trả lời bash thuần túy:

Đầu tiên thay đổi thư mục mà bạn đã sao chép các tập tin.

cd dest

Sau đó, lsthư mục nguồn và dẫn đầu ra vàorm

ls source | xargs rm


1
phân tích cú pháp lsthường là một ý tưởng tồi. mywiki.wooledge.org/ParsingLs Tôi sẽ không tải bài viết của bạn, nhưng nó phải được lưu ý.
Sergiy Kolodyazhnyy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.