Câu trả lời:
Lớp lót đơn giản này sẽ hoạt động trong mọi lớp vỏ, không chỉ bash:
ls -1q log* | wc -l
ls -1q sẽ cung cấp cho bạn một dòng trên mỗi tệp, ngay cả khi chúng chứa khoảng trắng hoặc ký tự đặc biệt như dòng mới.
Đầu ra được dẫn đến wc -l, tính số lượng dòng.
ls
, vì nó tạo ra một quá trình con. log*
được mở rộng bởi shell, không ls
, vì vậy một đơn giản echo
sẽ làm.
logs
trong thư mục được đề cập, thì nội dung của thư mục nhật ký đó cũng sẽ được tính. Điều này có lẽ không cố ý.
Bạn có thể thực hiện việc này một cách an toàn (nghĩa là sẽ không bị lỗi bởi các tệp có dấu cách hoặc \n
trong tên của chúng) với bash:
$ shopt -s nullglob
$ logfiles=(*.log)
$ echo ${#logfiles[@]}
Bạn cần kích hoạt nullglob
để không nhận được chữ *.log
trong $logfiles
mảng nếu không có tệp nào khớp. (Xem Cách "hoàn tác" một 'set -x'? Để biết ví dụ về cách đặt lại an toàn.)
shopt -u nullglob
nên được bỏ qua nếu nullglob
không được đặt thì bạn đã bắt đầu.
*.log
bằng chỉ *
sẽ đếm các thư mục. Nếu các tệp bạn muốn liệt kê có quy ước đặt tên truyền thống name.extension
, hãy sử dụng *.*
.
Rất nhiều câu trả lời ở đây, nhưng một số không tính đến
-l
)*.log
thay vìlog*
logs
phù hợp log*
)Đây là một 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, 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 các lối thoát kiểu C cho các ký tự không rõ ràng, chủ yếu gây ra các dòng mới được in dưới dạng \n
.-a
in ra tất cả các tệp, thậm chí các tệp bị ẩn (không thực sự cần thiết khi toàn cầu log*
ngụ ý không có tệp ẩn)-d
in ra các thư mục mà không cố gắng liệt kê các nội dung của thư mục, đó là những gì ls
thường làm-1
đảm bảo rằng nó nằm trên một cột (ls thực hiện điều này tự động khi ghi vào một đường ống, vì vậy nó không thực sự 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
việc liệt kê toàn bộ thư mục làm việc thay thế.)wc -l
tiêu thụ danh sách thư mục khi nó được tạo, do đó, đầu ra 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 cách sử dụng --
để không được hiểu là đối số ls
(trong trường hợp log*
bị xóa)Shell sẽ mở rộng log*
ra 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, do đó, chạy nó qua grep sẽ tốt hơn:
ls -Uba1 | grep ^log | wc -l
Cái cuối cùng này xử lý các thư mục cực lớn của các tệp mà không sử dụng nhiều bộ nhớ (mặc dù nó sử dụng một lớp con). Điều -d
này không còn cần thiết nữa, vì nó chỉ liệt kê nội dung của thư mục hiện tại.
Đối với một tìm kiếm đệ quy:
find . -type f -name '*.log' -printf x | wc -c
wc -c
sẽ đếm số lượng ký tự trong đầu ra của find
, trong khi -printf x
yêu find
cầu in một ký tự x
cho mỗi kết quả.
Đối với một tìm kiếm không đệ quy, hãy làm điều này:
find . -maxdepth 1 -type f -name '*.log' -printf x | wc -c
-name '*.log'
thì nó sẽ đếm tất cả các tệp, đó là những gì tôi cần cho trường hợp sử dụng của mình. Ngoài ra cờ -maxdepth cực kỳ hữu ích, cảm ơn!
find
; chỉ cần in một cái gì đó khác với tên tập tin nguyên văn.
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 cho 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ố lượng ký tự dòng mới và đếm chúng ngay cả khi chúng in ra thiết bị đầ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ử 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 khớp với mẫu có tên xảy ra để chứa ký tự dòng mới. Ví dụ:
touch log$'\n'def
ls log* -l | wc -l
Nếu bạn có nhiều tệp và bạn không muốn sử dụng shopt -s nullglob
giải pháp mảng thanh lịch và bash, bạn có thể sử dụng find và miễn là bạn không in tên tệp (có thể chứa dòng mới).
find -maxdepth 1 -name "log*" -not -name ".*" -printf '%i\n' | wc -l
Điều này sẽ tìm thấy tất cả các tệp khớp với nhật ký * và không bắt đầu bằng .*
- "không phải tên. *" Là phần mềm, nhưng điều quan trọng cần lưu ý là mặc định cho "ls" là không hiển thị tệp dấu chấm, nhưng mặc định để tìm là bao gồm chúng.
Đây là một 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 xung quanh giữa các lệnh.
Nhưng, shopt nullglob
câu trả lời là câu trả lời tốt nhất!
find
vs 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 thường là,
find
lẽ không có tất cả các tùy chọn ưa thích cho ls
cả hai.
-maxdepth 1
find
làm điều này theo mặc định. Điều này có thể tạo ra sự nhầm lẫn nếu người ta không nhận ra có một thư mục con bị ẩn và có thể giúp sử dụng nó ls
trong một số trường hợp không được báo cáo theo mặc định.
Đây là một lót của tôi cho việc này.
file_count=$( shopt -s nullglob ; set -- $directory_to_search_inside/* ; echo $#)
set --
không làm gì cả ngoại trừ việc chúng tôi sẵn sàng $#
, lưu trữ số lượng đối số dòng lệnh được truyền cho chương trình shell
(không đủ danh tiếng để bình luận)
Đây là BUGGY :
ls -1q some_pattern | wc -l
Nếu shopt -s nullglob
tình cờ được đặt, nó sẽ in số lượng TẤT CẢ các tệp thông thường, không chỉ các tệp có mẫu (được thử nghiệm trên CentOS-8 và Cygwin). Ai biết những con bọ vô nghĩa khác ls
có gì?
Đây là ĐÚNG và nhanh hơn nhiều:
shopt -s nullglob; files=(some_pattern); echo ${#files[@]};
Nó làm công việc mong đợi.
0.006
trên CentOS và 0.083
trên Cygwin (trong trường hợp nó được sử dụng cẩn thận).
0.000
trên CentOS và 0.003
trên Cygwin.
Bạn có thể định nghĩa một lệnh như vậy một cách dễ dàng, bằng cách sử dụng hàm shell. Phương pháp này không yêu cầu bất kỳ chương trình bên ngoài nào và không sinh ra bất kỳ quy trình con nào. Nó không thử ls
phân tích cú pháp nguy hiểm và xử lý các ký tự đặc biệt của người dùng (các khoảng trắng, dòng mới, dấu gạch chéo, v.v.) tốt. Nó chỉ dựa vào cơ chế mở rộng tên tệp được cung cấp bởi shell. Nó tương thích với ít nhất sh, bash và zsh.
Dòng bên dưới định nghĩa một hàm được gọi là count
in số lượng đối số mà nó được gọi.
count() { echo $#; }
Đơn giản chỉ cần gọi nó với mẫu mong muốn:
count log*
Để kết quả chính xác khi mẫu hình cầu không khớp, tùy chọn shell nullglob
(hoặc failglob
- đó là hành vi mặc định trên zsh) phải được đặt khi mở rộng thời gian xảy ra. Nó có thể được đặt như thế này:
shopt -s nullglob # for sh / bash
setopt nullglob # for zsh
Tùy thuộc vào những gì bạn muốn đếm, bạn cũng có thể quan tâm đến tùy chọn shell dotglob
.
Thật không may, với bash ít nhất, không dễ để đặt các tùy chọn này cục bộ. Nếu bạn không muốn đặt chúng trên toàn cầu, giải pháp đơn giản nhất là sử dụng chức năng theo cách phức tạp hơn này:
( shopt -s nullglob ; shopt -u failglob ; count log* )
Nếu bạn muốn khôi phục cú pháp nhẹ count log*
hoặc nếu bạn thực sự muốn tránh sinh ra một nhánh con, bạn có thể hack một cái gì đó dọc theo dòng:
# sh / bash:
# the alias is expanded before the globbing pattern, so we
# can set required options before the globbing gets expanded,
# and restore them afterwards.
count() {
eval "$_count_saved_shopts"
unset _count_saved_shopts
echo $#
}
alias count='
_count_saved_shopts="$(shopt -p nullglob failglob)"
shopt -s nullglob
shopt -u failglob
count'
Là một phần thưởng, chức năng này được sử dụng phổ biến hơn. Ví dụ:
count a* b* # count files which match either a* or b*
count $(jobs -ps) # count stopped jobs (sh / bash)
Bằng cách biến chức năng thành tệp tập lệnh (hoặc chương trình C tương đương), có thể gọi được từ PATH, nó cũng có thể được tạo bằng các chương trình như find
và xargs
:
find "$FIND_OPTIONS" -exec count {} \+ # count results of a search
Tôi đã đưa ra câu trả lời này rất nhiều suy nghĩ, đặc biệt là đưa ra những thứ không phân tích . Lúc đầu, tôi đã thử
<CẢNH BÁO! KHÔNG LÀM VIỆC>
du --inodes --files0-from=<(find . -maxdepth 1 -type f -print0) | awk '{sum+=int($1)}END{print sum}'
</ CẢNH BÁO! KHÔNG LÀM VIỆC>
mà làm việc nếu chỉ có một tên tệp như
touch $'w\nlf.aa'
nhưng thất bại nếu tôi tạo một tên tệp như thế này
touch $'firstline\n3 and some other\n1\n2\texciting\n86stuff.jpg'
Cuối cùng tôi đã nghĩ ra những gì tôi đặt bên dưới. Lưu ý Tôi đã cố gắng để có được một số lượng tất cả các tệp trong thư mục (không bao gồm bất kỳ thư mục con nào). Tôi nghĩ rằng, cùng với câu trả lời của @Mat và @Dan_Yard, cũng như có ít nhất hầu hết các yêu cầu được đặt ra bởi @mogsie (Tôi không chắc về bộ nhớ.) Tôi nghĩ rằng câu trả lời của @mogsie là chính xác, nhưng tôi luôn cố gắng tránh xa việc phân tích cú pháp ls
trừ khi đó là một tình huống cực kỳ cụ thể.
awk -F"\0" '{print NF-1}' < <(find . -maxdepth 1 -type f -print0) | awk '{sum+=$1}END{print sum}'
Dễ đọc hơn:
awk -F"\0" '{print NF-1}' < \
<(find . -maxdepth 1 -type f -print0) | \
awk '{sum+=$1}END{print sum}'
Điều này đang thực hiện tìm kiếm cụ thể cho các tệp, phân định đầu ra bằng ký tự null (để tránh các vấn đề với khoảng trắng và nguồn cấp dữ liệu), sau đó đếm số lượng ký tự null. Số lượng tệp sẽ ít hơn một ký tự null, vì cuối cùng sẽ có một ký tự null.
Để trả lời câu hỏi của OP, có hai trường hợp cần xem xét
1) Tìm kiếm không đệ quy:
awk -F"\0" '{print NF-1}' < \
<(find . -maxdepth 1 -type f -name "log*" -print0) | \
awk '{sum+=$1}END{print sum}'
2) Tìm kiếm đệ quy. Lưu ý rằng những gì bên trong -name
tham số có thể cần phải được thay đổi cho hành vi hơi khác nhau (các tệp ẩn, v.v.).
awk -F"\0" '{print NF-1}' < \
<(find . -type f -name "log*" -print0) | \
awk '{sum+=$1}END{print sum}'
Nếu bất cứ ai muốn bình luận về cách những câu trả lời này so với những câu tôi đã đề cập trong câu trả lời này, xin vui lòng làm.
Lưu ý, tôi đã nhận được quá trình suy nghĩ này trong khi nhận được câu trả lời này .
Đây là những gì tôi luôn luôn làm:
đăng nhập * | awk 'HẾT {in NR}'
awk 'END{print NR}'
nên tương đương với wc -l
.
ls -1 log* | wc -l
Có nghĩa là liệt kê một tệp trên mỗi dòng và sau đó chuyển nó thành lệnh đếm từ với chuyển tham số sang đếm dòng.
-l
, vì điều đó đòi hỏistat(2)
trên mỗi tệp và cho mục đích đếm không thêm gì.