Câu hỏi Có một lệnh bash mà đếm tập tin?


Có lệnh bash nào đếm số lượng tệp phù hợp với mẫu không?

Ví dụ: tôi muốn lấy tổng số tệp trong một thư mục khớp với mẫu này: log*


76
2017-07-03 08:35


gốc




Các câu trả lời:


Điều này một lớp lót đơn giản nên làm việc trong bất kỳ trình bao, không chỉ bash:

ls -1q log* | wc -l

ls -1q sẽ cung cấp cho bạn một dòng cho mỗi tệp, ngay cả khi chúng chứa khoảng trắng hoặc các ký tự đặc biệt, chẳng hạn như dòng mới.

Đầu ra là đường ống đến wc -l, đếm số dòng.


102
2017-07-03 08:41



Tôi sẽ không sử dụng -l, vì yêu cầu đó stat(2) trên mỗi tệp và cho mục đích đếm không thêm gì. - camh
Tôi sẽ không sử dụng ls, vì nó tạo ra một tiến trình con. log* được mở rộng bởi trình bao, chứ không phải ls, một cách đơn giản echo sẽ làm. - cdarke
mywiki.wooledge.org/ParsingLs - ormaaj
Trên thực tế "ls -l" không in các tệp trên mỗi dòng khi đầu ra được tạo đường ống. touch abc$'\n'def; ls abc*def | wc -l in 2 mặc dù nếu bạn không đặt nó, nó sẽ hiển thị abc?def. ls -1f có cùng một vấn đề. - mogsie
@ WalterTross Đó là sự thật (không phải hiệu quả đó là một yêu cầu của câu hỏi gốc). Tôi cũng chỉ tìm thấy rằng -q sẽ chăm sóc các tập tin với newlines, ngay cả khi đầu ra không phải là thiết bị đầu cuối. Và những lá cờ này được hỗ trợ bởi tất cả các nền tảng và hệ vỏ mà tôi đã thử nghiệm. Cập nhật câu trả lời, cảm ơn bạn và camh cho đầu vào! - Daniel


Bạn có thể thực hiện việc này một cách an toàn (tức là sẽ không bị các tệp có dấu cách hoặc bị lỗi \n trong tên của họ) với bash:

$ shopt -s nullglob
$ logfiles=(*.log)
$ echo ${#logfiles[@]}
$ shopt -u nullglob

Bạn cần bật nullglob để bạn không có được chữ *.log bên trong $logfiles  mảng nếu không có tệp nào phù hợp.


33
2017-07-03 08:43





Thử cái này:

echo *.log | wc -w


30
2017-07-03 08:41



echo *.log | wc -w sẽ cho kết quả không chính xác nếu một số tệp có dấu cách trong tên của chúng - lanzz
@lanzz: Đúng; đã không nghĩ về điều đó! - Will Vousden
Tôi chưa nộp với không gian - hudi
Thậm chí nếu bạn không có tệp có dấu cách, một số người dùng khác trong tập lệnh của bạn có thể gặp phải tệp được đặt tên độc hại, khiến các tập lệnh thất bại. Ngoài ra, những người khác gặp phải điều này trên StackOverflow có thể có các tệp tin với các dòng mới và cần biết những cạm bẫy. - mogsie


Rất nhiều câu trả lời ở đây, nhưng một số câu trả lời không tính đến

  • tên tệp có dấu cách, dòng mới hoặc ký tự điều khiển trong đó
  • tên tệp bắt đầu bằng dấu gạch ngang (hãy tưởng tượng một tệp có tên -l)
  • các tệp ẩn, bắt đầu bằng dấu chấm (nếu dấu *.log thay vì log*
  • các thư mục khớp với glob (ví dụ: một thư mục được gọi là logs phù hợp log*)
  • các thư mục trống (tức là kết quả là 0)
  • các thư mục cực kỳ lớn (liệt kê tất cả các thư mục có thể làm cạn kiệt bộ nhớ)

Đây là giải pháp xử lý tất cả chúng:

ls 2>/dev/null -Ubad1 -- log* | wc -l

Giải trình:

  • -U nguyên nhân ls để không sắp xếp các mục nhập, có nghĩa là nó không cần tải toàn bộ danh sách thư mục trong bộ nhớ
  • -b in dấu thoát kiểu C cho các ký tự không in ảnh, khiến cho các dòng mới được in thành \n.
  • -a in ra tất cả các tập tin, thậm chí cả các tập tin ẩn (không hoàn toàn cần thiết khi glob log* ngụ ý không có tệp ẩn)
  • -d in ra các thư mục mà không tìm cách liệt kê nội dung của thư mục, đó là những gì ls bình thường sẽ làm
  • -1 đảm bảo rằng nó trên một cột (ls thực hiện điều này tự động khi viết vào một đường ống, vì vậy nó không phải là cần thiết)
  • 2>/dev/null chuyển hướng stderr để nếu có 0 tệp nhật ký, bỏ qua thông báo lỗi. (Lưu ý rằng shopt -s nullglob sẽ gây ra ls để liệt kê toàn bộ thư mục hoạt động thay thế.)
  • wc -l tiêu thụ danh sách thư mục khi nó được tạo ra, vì vậy đầu ra của ls không bao giờ có trong bộ nhớ tại bất kỳ thời điểm nào.
  • -- Tên tệp được phân tách khỏi lệnh bằng -- để không được hiểu là đối số ls (trong trường hợp log* bị xóa)

Vỏ sẽ mở rộng log* vào danh sách đầy đủ các tệp, có thể làm cạn kiệt bộ nhớ nếu có nhiều tệp, vì vậy, sau đó chạy tệp thông qua grep thì tốt hơn:

ls -Uba1 | grep ^log | wc -l

Điều cuối cùng này xử lý các thư mục tệp cực lớn mà không cần sử dụng nhiều bộ nhớ (mặc dù nó sử dụng một vỏ bọc con). Các -d không còn cần thiết vì nó chỉ liệt kê nội dung của thư mục hiện tại.


29
2017-11-24 11:01



Câu trả lời tuyệt vời và cực kỳ chính xác. - raratiru


Câu trả lời được chấp nhận cho câu hỏi này là sai, nhưng tôi có đại diện thấp nên không thể thêm nhận xét vào câu hỏi đó.

Câu trả lời đúng cho câu hỏi này được đưa ra bởi Mat:

shopt -s nullglob
logfiles=(*.log)
echo ${#logfiles[@]}

Vấn đề với câu trả lời được chấp nhận là wc -l đếm số ký tự dòng mới và đếm chúng ngay cả khi chúng in tới đầu cuối là '?' trong đầu ra của 'ls -l'. Điều này có nghĩa là câu trả lời được chấp nhận FAILS khi tên tệp chứa ký tự dòng mới. Tôi đã thử nghiệm lệnh được đề xuất:

ls -l log* | wc -l

và nó báo cáo sai giá trị là 2 ngay cả khi chỉ có 1 tệp phù hợp với mẫu có tên sẽ chứa ký tự dòng mới. Ví dụ:

touch log$'\n'def
ls log* -l | wc -l

5
2017-10-30 06:11





Nếu bạn có nhiều tệp và bạn không muốn sử dụng shopt -s nullglob và bash giải pháp mảng, bạn có thể sử dụng tìm và vv miễn là bạn không in ra tên tệp (có thể chứa dòng mới).

find -maxdepth 1 -name "log*" -not -name ".*" -printf '%i\n' | wc -l

Thao tác này sẽ tìm tất cả các tệp khớp với nhật ký * và không bắt đầu bằng .* - "Tên không. *" Là redunant, nhưng điều quan trọng cần lưu ý là mặc định cho "ls" là không hiển thị dot-files, nhưng mặc định cho tìm là bao gồm chúng.

Đây là câu trả lời đúng và xử lý bất kỳ loại tên tệp nào bạn có thể ném vào nó, bởi vì tên tệp không bao giờ được chuyển giữa các lệnh.

Nhưng shopt nullglob câu trả lời là câu trả lời hay nhất!


4
2017-08-22 19:16



Bạn có thể nên cập nhật câu trả lời ban đầu của bạn thay vì trả lời một lần nữa. - qodeninja
Tôi nghĩ sử dụng find so với sử dụng ls là hai cách khác nhau để giải quyết vấn đề. find không phải lúc nào cũng có mặt trên máy, nhưng ls thường là, - mogsie


Đây là một lớp lót của tôi cho việc này.

 file_count=$( shopt -s nullglob ; set -- $directory_to_search_inside/* ; echo $#)

2
2017-11-04 19:48





ls -1 log* | wc -l

Điều này có nghĩa là liệt kê một tệp trên mỗi dòng và sau đó đặt nó vào lệnh đếm từ với chuyển đổi tham số để đếm các dòng.


1
2017-07-03 20:17



"-1" tùy chọn là không cần thiết khi đường ống đầu ra ls. Nhưng bạn có thể muốn ẩn thông báo lỗi ls nếu không có tệp nào phù hợp với mẫu. Tôi đề nghị "ls log * 2> / dev / null | wc -l". - JohnMudd