Loại trừ * trong dòng lệnh


14

Có rất nhiều tình huống trong đó việc sử dụng a *hầu như không thể tránh khỏi - ví dụ rm -rf *trong một thư mục chứa hàng ngàn thư mục con và tệp.

Nhưng nếu bạn muốn loại trừ chỉ một hoặc hai tệp hoặc thư mục khỏi rmlệnh thì sao? Tôi đã tìm cách và chỉ tìm thấy các giải pháp khá phức tạp find . -depth -not \( -name 'one' -o -name 'two' \ -o -name 'three' \) -exec rm {} \;như đã nêu ở đây .

Có khả năng để làm điều này một cách dễ dàng hơn - mà không có đường vòng đến findkhông? Ví dụ rm -rf --exclude='one' --exclude='two' --exclude='three' *như trong rsync hay chỉ rm -rf -e 'one','two','three' *?

Thậm chí có thể một khả năng tổng quát để loại trừ mọi thứ từ *(lệnh để khác thích cp, mv... không cần phải thực hiện riêng của họ)? Một cái gì đó như thế *{'one','two','three'}hay như vậy?


3
Nếu bạn chỉ muốn giữ một hoặc hai tệp thì cách đơn giản và dễ nhất là di chuyển các tệp đó sang vị trí khác, xóa sạch tất cả các tệp còn lại và di chuyển các tệp bạn giữ lại. Nếu bạn muốn giữ nhiều tệp hơn, tôi sẽ sử dụng findvới --deletetùy chọn (không cần thực thi rmcho mỗi tệp. Đó là chi phí không cần thiết).
Hennes

Đó sẽ là một khả năng, nhưng nó gần như công phu như tôi đã đề cập. Tôi biết tôi có thể kết hợp các lệnh với một cái gì đó như thế nào mv -t /tmp one two three && rm -rf * && mv -t . /tmp/one /tmp/two /tmp/three, nhưng tôi thích một giải pháp cho khả năng loại trừ một cách rõ ràng thứ gì đó khỏi *. Chắc chắn sẽ có những tình huống di chuyển hoặc sao chép các tệp đến một đích khác sẽ không phải là một lựa chọn.
David

2
Đó là, đó là lý do tại sao tôi không đăng nó như một câu trả lời. Chỉ đơn thuần là một cách làm việc ít phức tạp hơn (ba lệnh rất đơn giản so với một lệnh phức tạp hơn một chút). Tôi ghét sự phức tạp kết hợp với rm.
Hennes

Câu trả lời:


33

Đối với Bash, có một tùy chọn shell được gọi extglobbị tắt theo mặc định để giữ tính tương thích với cú pháp shell tiêu chuẩn. Extglob bổ sung cho các cú pháp của các nhà khai thác bổ sung như !(), ?(), @()và một số chi tiết.

Để bật extglob, gõ shopt -s extglob. Để giữ cho nó được bật cho loại người dùng hiện tại echo 'shopt -s extglob' >> ~/.bashrc.

Ví dụ về rm: Với extglob bạn có thể sử dụng

rm -rf !(one|two|three)

Có vẻ như là một giải pháp tốt, nhưng tôi cũng không muốn bật và tắt nó mỗi khi tôi gặp tình huống như mô tả.
David

3
@David: bạn có thể đặt shoptthứ đó vào ~/.bashrc, nó không thể làm tổn thương.
enzotib

Nếu tôi hiểu đúng, tùy chọn extglob hoàn toàn không thay đổi hiệu ứng *, mà thay vào đó thêm các chỉ số tương tự (như !) có chức năng bổ sung? Trong trường hợp đó, nó sẽ là một giải pháp đáng yêu.
David

1
@David: Chính xác.
choroba

1
@David Để giữ khả năng tương thích với cú pháp shell tiêu chuẩn . Có khả năng các tình huống khác mà hành vi có thể thay đổi.
gerrit

4

Tôi đã xây dựng ngoại trừ chính xác cho điều đó (vì tôi chưa thể thêm tài liệu).

Tôi có một thư mục như sau:

$ ls
a b c d

except b dsẽ cho bạn ac

$ except b d
ac

Bây giờ bạn có thể dẫn nó đến rm.

except b d | xargs -0 rm

Điều này có thể khó nhớ, vậy tại sao không xây dựng một tập lệnh trên đầu ngoại trừ, được gọi là rm-except:

#!/usr/bin/env bash

except "$@" | xargs -0 rm

Tương tự dễ dàng là ls-except:

#!/usr/bin/env bash

except "$@" | xargs -0 ls

3
Điều này thật tuyệt, nhưng không thực sự cần thiết khi bạn biết về extglob - askubfox.com/a/329233/138369
David

1

Thay thế cho extglob (mặc dù đó là một câu trả lời rất hay và mọi người nên có shopt -s extglob globstartrong .bashrc của họ), bạn có thể sử dụng biến toàn cầu $ GLOBIGNORE . Giả sử bạn muốn nhận mọi tệp ngoại trừ 'foo.txt' và 'bar baz.txt':

GLOBIGNORE=foo.txt:'bar baz.txt'

... tuy nhiên, điều này sẽ bật tùy chọn shell dotglob, có nghĩa là *sẽ khớp các tệp bắt đầu bằng dấu chấm (thường được ẩn). Vì vậy, bạn thực sự cần hai lệnh:

GLOBIGNORE=foo.txt:'bar baz.txt'
shopt -u dotglob

Vì đây là biến toàn cục, nên nó sẽ ảnh hưởng đến mọi toàn cầu bạn sử dụng cho đến khi $ GLOBSTAR không được đặt bằng cách đăng xuất hoặc với

GLOBIGNORE=

Nó cũng sẽ chỉ hoạt động trên các chuỗi ký tự được truyền cho biến. Bạn có thể thấy những gì tôi muốn nói bằng cách đặt $ GLOBIGNORE và xem sự khác biệt giữa các lệnh này:

printf '%s\n' *
printf '%s\n' ./*
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.