xóa tệp nhưng loại trừ tất cả các tệp trong danh sách


16

Tôi cần dọn dẹp một thư mục định kỳ. Tôi nhận được một filelist có chứa văn bản, tập tin nào được cho phép. Bây giờ tôi phải xóa tất cả các tệp không có trong tệp này.

Thí dụ:

dont-delete.txt:

dontdeletethisfile.txt
reallyimportantfile.txt
neverdeletethis.txt
important.txt

Thư mục của tôi làm sạch có chứa điều này như ví dụ:

ls /home/me/myfolder2tocleanup/:

dontdeletethisfile.txt
reallyimportantfile.txt
neverdeletethis.txt
important.txt
this-can-be-deleted.txt
also-waste.txt
never-used-it.txt

Vì vậy, các tập tin này nên được xóa:

this-can-be-deleted.txt
also-waste.txt
never-used-it.txt

Tôi tìm kiếm một cái gì đó để tạo một lệnh xóa với một tùy chọn để loại trừ một số tệp được cung cấp bởi tệp.


Đây có phải là một bài tập về nhà?
mook765

Tôi hy vọng bạn không phải là giáo viên của mình. lol
Gujarat Santana

2
@gujarat Chúng tôi không phải là dịch vụ bài tập về nhà miễn phí, vì vậy nhận xét là hợp lý. Đối với bản thân câu hỏi, nó có thể hữu ích cho người khác, vì vậy nó mở cho đến nay.
Sergiy Kolodyazhnyy

@Serg Tôi hoàn toàn đồng ý với bạn
Gujarat Santana

Câu trả lời:


8

Các rmlệnh là nhận xét ra để bạn có thể kiểm tra và xác minh rằng nó hoạt động khi cần thiết. Sau đó chỉ cần bỏ bình luận dòng đó.

Phần check directorynày sẽ đảm bảo bạn không vô tình chạy tập lệnh từ thư mục sai và ghi đè các tệp sai.

Bạn có thể loại bỏ echo deletingdòng để chạy âm thầm.

#!/bin/bash

cd /home/me/myfolder2tocleanup/

# Exit if the directory isn't found.
if (($?>0)); then
    echo "Can't find work dir... exiting"
    exit
fi

for i in *; do
    if ! grep -qxFe "$i" filelist.txt; then
        echo "Deleting: $i"
        # the next line is commented out.  Test it.  Then uncomment to removed the files
        # rm "$i"
    fi
done

Tôi đã chỉnh sửa mã của bạn để tránh việc sử dụng vô íchls và việc nắm bắt đầu ra vô ích grepnếu tất cả những gì bạn muốn biết là liệu có khớp hay không. Tôi cũng đã sử dụng các mẫu chuỗi cố định để tránh thoát các vấn đề.
David Foerster

@DavidFoerster Cảm ơn sự đóng góp. Tuy nhiên, khi bạn thay đổi whilevòng lặp thành forvòng lặp, bạn vô tình thay đổi iteration keytừ ithành f. trong khai báo, đã phá vỡ mã. Tôi sửa nó rồi.
LD James

Rất tiếc, lực lượng của thói quen. Tôi có xu hướng viết tắt tên biến shell cho tên tệp là f. ;-P (Từ và +1 cho câu trả lời của bạn mà tôi đã quên trước đó.)
David Foerster

10

Kịch bản python này có thể làm điều này:

#!/usr/bin/env python3
import os
no_remove = set()
with open('./dont-delete.txt') as f:
     for line in f:
         no_remove.add(line.strip())

for f in os.listdir('.'):
    if f not in no_remove:
        print('unlink:' + f ) 
        #os.unlink(f)

Phần quan trọng là để bỏ sót os.unlink()chức năng.

LƯU Ý : thêm tập lệnh này và dont-delete.txtvào tập lệnh của bạn dont-delete.txtđể cả hai đều có trong danh sách và giữ chúng trong cùng một thư mục.


1
Tôi đã thay đổi mã của bạn để sử dụng setthay vì danh sách cho O (1) thay vì tra cứu O (n) trong phần thứ hai.
David Foerster

cảm ơn sự giúp đỡ của bạn, bình thường tôi là một anh chàng cửa sổ, nhưng đường may trăn quá tuyệt =)
stefan83

1
@ stefan83: Python cũng chạy trên Windows.
David Foerster

3

Đây là một lót:

comm -2 -3 <(ls) <(sort dont_delete) | tail +2 | xargs -p rm
  1. ls in tất cả các tệp trong thư mục hiện tại (theo thứ tự sắp xếp)
  2. sort dont_delete in tất cả các tệp chúng tôi không muốn xóa theo thứ tự sắp xếp
  3. các <()nhà điều hành chuyển một chuỗi thành một đối tượng tập tin giống như
  4. Các commlệnh so sánh hai tệp được sắp xếp trước và in ra các dòng mà chúng khác nhau
  5. sử dụng các -2 -3cờ gây ra commchỉ in các dòng có trong tệp đầu tiên chứ không phải các tệp thứ hai, đây sẽ là danh sách các tệp an toàn để xóa
  6. các tail +2cuộc gọi chỉ là để loại bỏ các tiêu đề của commsản lượng, trong đó có tên của tập tin đầu vào
  7. Bây giờ chúng tôi nhận được một danh sách các tập tin để xóa trên tiêu chuẩn. Chúng tôi dẫn đầu ra xargsnày sẽ biến luồng đầu ra thành một danh sách các đối số cho rm. Các -ptùy chọn buộc xargsphải yêu cầu xác nhận trước khi thực hiện.

Thx giúp bạn, bây giờ tôi có giải pháp của tôi!
stefan83

@gardenhead, tôi mệt mỏi với mã của bạn nhưng nó sẽ xóa tất cả các tệp trong thư mục và chỉ giữ lại tệp đầu tiên và tệp cuối cùng trong danh sách không xóa. Bạn có ý tưởng nào cho vấn đề này không? cảm ơn trước.
Negar

1

FWIW có vẻ như bạn có thể thực hiện điều này một cách tự nhiên zshbằng cách sử dụng (+cmd)vòng loại toàn cầu.

Để minh họa, hãy bắt đầu với một số tệp

 % ls
bar  baz  bazfoo  keepfiles.txt  foo  kazoo

và một tập tin danh sách trắng

 % cat keepfiles.txt
foo
kazoo
bar

Đầu tiên, đọc danh sách trắng thành một mảng:

 % keepfiles=( "${(f)$(< keepfiles.txt)}" )

hoặc có lẽ tốt hơn

 % zmodload zsh/mapfile
 % keepfiles=( ${(f)mapfile[./keepfiles.txt]} )

(tương đương với nội dung của bash mapfile- hoặc từ đồng nghĩa của nó readarray). Bây giờ chúng ta có thể kiểm tra xem một khóa (tên tệp) có tồn tại trong mảng hay không bằng cách ${keepfiles[(I)filename]}trả về 0 nếu không tìm thấy kết quả khớp nào:

 % print ${keepfiles[(I)foo]}
1
 % print ${keepfiles[(I)baz]}
0
 %

Chúng ta có thể sử dụng điều này để tạo một hàm trả về truenếu không có kết quả khớp nào $REPLYtrong mảng:

% nokeep() { (( ${keepfiles[(I)$REPLY]} == 0 )); }

Cuối cùng, chúng tôi sử dụng chức năng này như một vòng loại trong lệnh của chúng tôi:

 % ls *(+nokeep)
baz  bazfoo  keepfiles.txt

hoặc, trong trường hợp của bạn

 % rm -- *(+nokeep)

(Bạn có thể muốn thêm tên của tệp danh sách trắng vào danh sách trắng.)


0

Giả sử rằng bash shell của bạn đã được extglob shoptthiết lập, đây là một cách thay thế có phần bảo thủ hơn:

rm !($(tr \\n \| < keep.txt))

(... Đi kèm với đề xuất hoa hồng tuyệt vời của @ gardenenhead!)


0

Giả sử không có khoảng trắng (Spaces / Tab) trong tệp của bạn 'được liệt kê trong tệp được gọi list, thì bạn sẽ làm:

find /path/to -type f \( ! -name "list" $(printf ' -a ! -name %s\n' $(< list)) \)

Chỉ cần thêm -deletevào lệnh trên để xóa các tệp không tồn tại trong tệp danh sách . Nếu tìm thấy bạn không có -deletetùy chọn mà bạn có thể sử dụng rmvới -execnhư sau:

find /path/to -type f \( ! -name "list" $(printf ' -a ! -name %s\n' $(< list)) \) -exec echo rm {} \;

Hoặc sử dụng -execvới +terminator thay thế.

find /path/to -type f \( ! -name "list" $(printf ' -a ! -name %s\n' $(< list)) \) -exec echo rm {} +

echo chỉ được sử dụng để chạy khô.


0

Trừ khi đầu ra ls /home/me/myfolder2tocleanup/vượt quá giới hạn đối số shell tối đa ARG_MAX khoảng 2 MB cho Ubuntu, tôi sẽ đề xuất như sau.


Việc thực hiện lệnh một dòng sẽ thực hiện công việc, như sau:

  1. Sao chép dont-delete.txttệp vào thư mục chứa các tệp sẽ bị xóa như vậy:
cp dont-delete.txt /home/me/myfolder2tocleanup/
  1. cd vào thư mục chứa các tệp sẽ bị xóa như vậy:
cd /home/me/myfolder2tocleanup/
  1. Thực hiện chạy thử để kiểm tra lệnh và làm cho nó in tên của các tệp mà nó phát hiện sẽ bị xóa mà không thực sự xóa chúng, như vậy:
ls -p | grep -v / | sed 's/\<dont-delete.txt\>//g' | sort | comm -3 - <(sort dont-delete.txt) | xargs echo | tr " " "\n"
  1. Nếu bạn hài lòng với đầu ra, hãy xóa các tệp bằng cách chạy lệnh như sau:
ls -p | grep -v / | sed 's/\<dont-delete.txt\>//g' | sort | comm -3 - <(sort dont-delete.txt) | xargs rm

Giải thích:

  • ls -psẽ liệt kê tất cả các tệp và thư mục trong thư mục hiện tại và tùy chọn -psẽ thêm a /vào tên thư mục.
  • grep -v /sẽ loại trừ các thư mục bằng cách xóa tất cả các mục có chứa một /trong tên của chúng.
  • sed 's/\<dont-delete.txt\>//g'sẽ loại trừ dont-delete.txttệp, vì vậy nó không bị xóa trong quá trình này.
  • sortsẽ, chỉ để đảm bảo, sắp xếp đầu ra còn lại của ls.
  • comm -3 - <(sort dont-delete.txt)sẽ sắp xếp dont-delete.txttệp, so sánh nó với đầu ra được sắp xếp lsvà loại trừ tên tệp tồn tại trong cả hai.
  • xargs rmsẽ loại bỏ tất cả các tên tệp còn lại trong đầu ra đã được xử lý ls. Đây có nghĩa là tất cả các mục trong thư mục hiện tại sẽ được loại bỏ trừ các thư mục , các file được liệt kê trong dont-delete.txttập tincác dont-delete.txttập tin bản thân

Trong phần chạy khô:

  • xargs echo sẽ in các tập tin cần được loại bỏ.
  • tr " " "\n" sẽ dịch các khoảng trắng thành các dòng mới để dễ đọc hơn.

-1

Đề nghị của tôi là:

sed -e 's/^/\.\//' dont-delete.txt > dont-delete-relative-path.txt
find . -type f -print | grep -Fxvf dont-delete-relative-path.txt | xargs -d'\n' rm

Cập nhật 2018-08-07

Thí dụ:

1: mkdir /tmp/delete-example && cd /tmp/delete-example
2: touch a b c d
3: echo "./a\n./b\n./dont-delete.txt\n" > dont-delete.txt
4: find . -type f -print | grep -Fxvf dont-delete.txt | xargs -d'\n' rm

Lưu ý sau dòng 3, bạn sẽ có dont-delete.txttệp có nội dung:

./a
./b
./dont-delete.txt

(hàng đầu ./rất quan trọng )

Các tập tin cdsẽ bị xóa.


Tôi đã thử điều này với một tệp văn bản của các tên tệp được phân tách bằng một dòng mới. Nó đã kết thúc việc xóa tất cả các tập tin trong thư mục.
Jacques MALAPRADE

Tôi đoán "danh sách giữ" của bạn là sai.
nyxz

Tôi đã thêm cách sử dụng ví dụ.
nyxz
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.