Cách nhanh chóng để xóa các tệp có ít hơn x dòng


10

Cách nhanh và không quá phức tạp để xóa tất cả các tệp trong một thư mục dài dưới x dòng, trong bash?

Câu trả lời:


10

Đây là một giải pháp POSIX khá dễ hiểu:

find . -type f -exec awk -v x=10 'NR==x{exit 1}' {} \; -exec echo rm -f {} \;

Như trong câu trả lời của Stephane , hãy loại bỏ echokhi hài lòng với những gì sẽ bị xóa.


Giải thích, được viết cho những người hoàn toàn mới đối với Unix / Linux:

Dấu chấm .đại diện cho thư mục hiện tại. findtìm thấy các tập tin và thư mục đệ quy bên trong ., và có thể làm mọi thứ với chúng.

-typelà một trong những find's bầu cử sơ bộ ; đó là một bài kiểm tra sẽ được thực hiện cho từng tệp và thư mục được tìm thấy đệ quy (bên trong .) và phần còn lại của các nguyên tắc trên dòng chỉ được đánh giá nếu kết quả này là "đúng".

Trong trường hợp cụ thể này, chúng tôi chỉ tiếp tục nếu chúng tôi đang xử lý một tệp thông thường , không phải thư mục hoặc thứ gì khác (ví dụ: thiết bị khối.)


Chính -exec(của find) gọi một lệnh bên ngoài và chỉ tiến hành chính tiếp theo nếu lệnh bên ngoài thoát thành công (trạng thái thoát của "0"). Tên {}được thay thế bằng tên tệp được "xem xét" bởi findlệnh. Vì vậy, -execcuộc gọi đầu tiên tương đương với lệnh shell sau, lần lượt được thực hiện cho từng tệp:

awk -v x=10 'NR==x{exit 1}' ./somefilename

Awk là toàn bộ ngôn ngữ, được thiết kế để xử lý các tệp văn bản được phân tách như CSV. Các điều kiện và lệnh Awk (được chứa giữa các dấu ngoặc đơn và bắt đầu bằng các chữ cái NR) được thực thi cho mỗi dòng của tệp văn bản. (Lặp lại ngầm định.)

Để tìm hiểu Awk đầy đủ, tôi rất khuyến khích Hướng dẫn Grymoire , nhưng tôi sẽ giải thích các tính năng Awk được sử dụng trong lệnh trên.


Các -vlá cờ để AWK cho phép chúng ta thiết lập một biến AWK (một lần) trước khi các lệnh AWK được thực hiện (đối với mỗi dòng của tập tin.) Trong trường hợp này, chúng tôi thiết lập xđể 10.


NRlà một biến Awk đặc biệt đề cập đến " số N của R hiện tại ecord ." Nói cách khác, đó là số dòng chúng ta đang xem xét trong bất kỳ đoạn cụ thể nào đi qua vòng lặp.

(Lưu ý rằng nó có thể, mặc dù không bình thường, sử dụng một "khác nhau R ecord S eparator" hơn so với mặc định của một ký tự xuống dòng, bởi khung cảnh RS. Dưới đây là một ví dụ về chơi với dải phân cách kỷ lục. )


Các tập lệnh Awk nói chung bao gồm các điều kiện (bên ngoài dấu ngoặc nhọn) kết hợp với các hành động (bên trong dấu ngoặc nhọn.) Có thể có các điều kiện ghép và hành động ghép và có một điều kiện mặc định (đúng) và hành động mặc định (in), nhưng chúng ta cần Không bận tâm với những điều đó.

Điều kiện ở đây là "Đây có phải là dòng thứ 10 không?" Nếu đây là trường hợp, chúng tôi thoát với trạng thái thoát khác không, trong kịch bản shell có nghĩa là "chấm dứt lệnh không thành công."

Do đó , cách duy nhất lệnh Awk này sẽ thoát thành công là nếu kết thúc tập tin trước khi đạt đến dòng thứ 10.

Vì vậy, nếu tập lệnh Awk thoát thành công, điều đó có nghĩa là bạn có một tệp có ít hơn mười dòng.


Cuộc -execgọi tiếp theo (nếu bạn loại bỏ echo) sẽ xóa từng tệp (sẽ tiến xa đến mức đánh giá các nguyên tắc find) bằng cách chạy:

rm -f ./somefilename

5

Giả sử findviệc triển khai hỗ trợ biến -readablevị ngữ (nếu bạn findkhông hỗ trợ nó, chỉ cần xóa nó, bạn sẽ chỉ nhận được thông báo lỗi cho các tệp không thể đọc hoặc thay thế bằng -exec test -r {} \;):

x=10 find . -type f -readable -exec sh -c '
  for file do
    lines=$(wc -l < "$file") && [ "$((lines))" -lt "$x" ] && echo rm -f "$file"
  done' sh {} +

Gỡ bỏ echo nếu hạnh phúc.

Điều đó không đặc biệt hiệu quả ở chỗ nó đếm tất cả các dòng trong mỗi tệp trong khi nó chỉ cần dừng lại ở dòng xthứ nhất và nó chạy một wc(và có khả năng là mộtrm ) cho mỗi tệp.

Với GNU awk, bạn có thể làm cho nó hiệu quả hơn rất nhiều với:

x=10
find . -type f -readable -exec awk -v x="$x" -v ORS='\0' '
  FNR == x {nextfile}
  ENDFILE {if (FNR < x) print FILENAME}' {} +|
  xargs -r0 echo rm -f

(một lần nữa, loại bỏ echo khi hạnh phúc).

Tương tự với perl:

x=10 find . -type f -readable -exec perl -Tlne '
  if ($. == $ENV{x}) {close ARGV}
  elsif (eof) {print $ARGV; close ARGV}' {} +

Thay thế printbằng unlinknếu hạnh phúc.


1. Cái cuối cùng shđể làm gì? 2. Có wc -l < "$file"nhanh hơn wc -l "$file"không? 3. Làm thế nào để sh biết giá trị của $x, được định nghĩa trong shell Bash gọi?

3
@tomas, cuối cùng shlà những gì diễn ra trong tập lệnh nội tuyến đó $0, được sử dụng cho các thông báo lỗi chẳng hạn. wc -l "$file"sẽ in tên tệp mà chúng tôi không muốn ở đây và sẽ chạy wcngay cả khi tệp không thể được mở. $xđược xuất sang find( x=10 find...) mà chính nó chuyển qua nó sh.
Stéphane Chazelas

Cảm ơn! Nhưng tôi đoán lỗi này mà tôi gặp phải trên OSX có nghĩa là phiên bản Bash của tôi không hỗ trợ cờ có thể đọc được? find: -readable: unknown primary or operator.
durrrutti

1
@durrrutti, điều đó không xuống bash. bashchỉ là một trình thông dịch dòng lệnh, nhưng của việc findthực hiện. -readablelà một phần mở rộng GNU, không có sẵn trong OS / X find. Nó chỉ được sử dụng để giới hạn các tệp có thể đọc được (bạn sẽ không thể lấy số lượng dòng cho các tệp không thể đọc được). Bạn có thể bỏ qua nó cho lần đầu tiên, sau đó bạn sẽ nhận được thông báo lỗi khi mở các tệp wccho các tệp không thể đọc được.
Stéphane Chazelas

@ StéphaneChazelas, câu trả lời này rất khó hiểu Tôi tự hỏi: Tôi có bỏ lỡ bất kỳ trường hợp cạnh nào với câu trả lời của tôi không? :)
tự đại diện

2

Để hoàn thiện, bỏ qua AWK, bạn cũng có thể sử dụng GNU sed để đạt được kết quả tương tự:

find . -type f -exec sed 11q1 '{}' ';' -exec echo rm -f '{}' ';'

Mà kết quả trong một dòng lệnh ngắn gọn hơn một chút.

Giải trình

11 - is the address, i.e. "the eleventh line"
q - is for _q_uit (abort the execution)
1 - is the exit code parameter for q (GNU sed extension) 
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.