Làm thế nào để báo cáo số lượng tệp trong tất cả các thư mục con?


24

Tôi cần kiểm tra tất cả các thư mục con và báo cáo có bao nhiêu tệp (không cần đệ quy thêm):

directoryName1 numberOfFiles
directoryName2 numberOfFiles

Tại sao bạn muốn sử dụng findkhi Bash sẽ làm gì? (shopt -s dotglob; for dir in */; do all=("$dir"/*); echo "$dir: ${#all[@]}"; done): cho tất cả các thư mục, đếm số lượng mục trong thư mục đó (bao gồm các tệp dấu chấm ẩn, ngoại trừ ...)
janmoesen

@janmoesen Tại sao bạn không đưa ra câu trả lời? Tôi chưa quen với kịch bản shell, nhưng tôi không thể thấy bất kỳ vấn đề nào với phương pháp của bạn. Đối với tôi, nó có vẻ là cách tốt nhất. Không ai đánh giá cao nhận xét của bạn, nhưng không ai bình luận về lý do tại sao nó có thể xấu. Các câu trả lời nâng cao có nhiều đại diện hơn bạn vì vậy nó làm tôi tự hỏi liệu tôi có thiếu thứ gì không.
toxalot

@toxalot: Tôi không bận tâm thêm nó dưới dạng câu trả lời vì nó quá ngắn (và có thể hơi hạ giọng). Hãy thoải mái để bình luận. :-) Ngoài ra, câu hỏi hơi mơ hồ liên quan đến "bao nhiêu tệp" nghĩa là gì. Giải pháp của tôi đếm các tập tin thư mục "thông thường" ; có lẽ poster thực sự có nghĩa là "tập tin, không phải thư mục". Một lưu ý khác là tính năng toàn cầu này không tính đến các tệp chấm "ẩn". Có nhiều cách xung quanh cả hai vấn đề. Nhưng một lần nữa: không chắc chắn về yêu cầu chính xác của người gửi ban đầu.
janmoesen

Câu trả lời:


30

Điều này làm nó một cách an toàn và di động. Nó sẽ không bị lẫn lộn bởi tên tập tin lạ.

for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \; | wc -l && echo $f; done

Lưu ý rằng nó sẽ in số lượng tệp trước, sau đó là tên thư mục trên một dòng riêng. Nếu bạn muốn giữ định dạng của OP, bạn sẽ cần định dạng thêm, vd

for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \;|wc -l|tr '\n' ' ' && echo $f; done|awk '{print $2"\t"$1}'

Nếu bạn có một bộ thư mục con cụ thể mà bạn quan tâm, bạn có thể thay thế *chúng bằng chúng.

Tại sao điều này an toàn? (và do đó xứng đáng với kịch bản)

Tên tệp có thể chứa bất kỳ nhân vật nào ngoại trừ /. Có một vài ký tự được xử lý đặc biệt bằng shell hoặc bằng các lệnh. Chúng bao gồm không gian, dòng mới và dấu gạch ngang.

Sử dụng for f in *cấu trúc là một cách an toàn để nhận từng tên tệp, bất kể nó chứa gì.

Một khi bạn có tên tệp trong một biến, bạn vẫn phải tránh những thứ như find $f. Nếu $fchứa tên tệp -test, findsẽ phàn nàn về tùy chọn bạn vừa đưa ra. Cách để tránh điều đó là bằng cách sử dụng ./trước tên; theo cách này, nó có cùng ý nghĩa, nhưng nó không còn bắt đầu bằng dấu gạch ngang.

Dòng mới và không gian cũng là một vấn đề. Nếu $fchứa "xin chào, bạn thân" là một tên tệp find ./$f, là find ./hello, buddy. Bạn đang nói findđể nhìn ./hello,buddy. Nếu những cái đó không tồn tại, nó sẽ phàn nàn, và nó sẽ không bao giờ nhìn vào ./hello, buddy. Điều này là dễ dàng để tránh - sử dụng dấu ngoặc kép xung quanh các biến của bạn.

Cuối cùng, tên tệp có thể chứa dòng mới, vì vậy việc đếm dòng mới trong danh sách tên tệp sẽ không hoạt động; bạn sẽ nhận được một số lượng bổ sung cho mỗi tên tệp với một dòng mới. Để tránh điều này, đừng đếm dòng mới trong danh sách các tệp; thay vào đó, hãy đếm các dòng mới (hoặc bất kỳ ký tự nào khác) đại diện cho một tệp. Đây là lý do tại sao findlệnh có đơn giản -exec echo \;và không -exec echo {} \;. Tôi chỉ muốn in một dòng mới cho mục đích kiểm đếm các tập tin.


1
Tại sao có một người trên thế giới sử dụng dòng mới trong tên tệp? Cảm ơn câu trả lời.
ShyBoy

1
Tên tập tin có thể chứa bất kỳ ký tự nào ngoại trừ / và ký tự null. dwheeler.com/essays/fixing-unix-linux-filenames.html
Flimm

2
Số lượng sẽ bao gồm các thư mục chính nó. Nếu bạn muốn loại trừ số đó khỏi số đếm, hãy sử dụng-mindepth 1
toxalot

Bạn cũng có thể sử dụng -printf '\n'thay vì -exec echo.
toxalot

1
@toxalot bạn có thể nếu bạn có một công cụ hỗ trợ -printf, nhưng không phải nếu bạn muốn nó hoạt động trên FreeBSD chẳng hạn.
Shawn J. Goff

6

Giả sử rằng bạn đang tìm kiếm một giải pháp Linux tiêu chuẩn, một cách tương đối đơn giản để đạt được điều này là find:

find dir1/ dir2/ -maxdepth 1 -type f | wc -l

Khi findđi qua hai thư mục con được chỉ định, đến -maxdepth1 sẽ ngăn đệ quy tiếp theo và chỉ báo cáo các tệp ( -type f) được phân tách bằng dòng mới. Kết quả sau đó được dẫn đến wcđể đếm số lượng các dòng đó.


Tôi có nhiều hơn 2 thư mục ... Làm thế nào tôi có thể kết hợp lệnh của bạn với find . -maxdepth 1 -type dđầu ra?
ShyBoy

Bạn có thể (a) bao gồm các thư mục cần thiết trong một biến và find $dirs ...hoặc (b) nếu chúng chỉ có trong thư mục cấp cao hơn, toàn cầu từ thư mục đó,find */ ...
jasonwryan

1
Điều này sẽ báo cáo kết quả không chính xác nếu bất kỳ tên tệp nào có ký tự dòng mới trong đó.
Shawn J. Goff

@Shawn: cảm ơn. Tôi nghĩ rằng tôi đã có tên tệp với không gian được che kín, nhưng chưa xem xét các dòng mới: có đề xuất nào để khắc phục không?
jasonwryan

Thêm vào -exec echolệnh find của bạn - theo cách đó nó không lặp lại tên tệp, chỉ là một dòng mới.
Shawn J. Goff

5

Bởi không có đệ quy, bạn có nghĩa là nếu directoryName1có thư mục con, thì bạn không muốn đếm các tệp trong thư mục con? Nếu vậy, đây là một cách để đếm tất cả các tệp thông thường trong các thư mục được chỉ định:

count=0
for d in directoryName1 directoryName2; do
  for f in "$d"/* "$d"/.[!.]* "$d"/..?*; do
    if [ -f "$f" ]; then count=$((count+1)); fi
  done
done

Lưu ý rằng -fkiểm tra thực hiện hai chức năng: nó kiểm tra xem mục nhập có khớp với một trong các khối trên không là tệp thông thường và kiểm tra xem mục đó có khớp hay không (nếu một trong số các khối đó không khớp với nhau, thì mẫu vẫn giữ nguyên như vậy). Nếu bạn muốn đếm tất cả các mục trong các thư mục đã cho bất kể loại của chúng, hãy thay thế -fbằng -e.

Ksh có một cách để làm cho các mẫu khớp với các tệp chấm và để tạo một danh sách trống trong trường hợp không có tệp nào khớp với một mẫu. Vì vậy, trong ksh bạn có thể đếm các tệp thông thường như thế này:

FIGNORE='.?(.)'
count=0
for x in ~(N)directoryName1/* ~(N)directoryName2/*; do
  if [ -f "$x" ]; then ((++count)); fi
done

hoặc tất cả các tệp chỉ đơn giản như thế này:

FIGNORE='.?(.)'
files=(~(N)directoryName1/* ~(N)directoryName2/*)
count=${#files}

Bash có nhiều cách khác nhau để làm cho điều này đơn giản hơn. Để đếm các tập tin thông thường:

shopt -s dotglob nullglob
count=0
for x in directoryName1/* directoryName2/*; do
  if [ -f "$x" ]; then ((++count)); fi
done

Để đếm tất cả các tệp:

shopt -s dotglob nullglob
files=(directoryName1/* directoryName2/*)
count=${#files}

Như thường lệ, nó thậm chí còn đơn giản hơn trong zsh. Để đếm các tập tin thông thường:

files=({directoryName1,directoryName2}/*(DN.))
count=$#files

Thay đổi (DN.)để (DN)đếm tất cả các tập tin.

¹ Lưu ý rằng mỗi mẫu phù hợp với bản thân, nếu không thì kết quả có thể được tắt (ví dụ như nếu bạn file đếm đang bắt đầu bằng một chữ số, bạn có thể không chỉ làm for x in [0-9]*; do if [ -f "$x" ]; then …vì có thể có một tập tin gọi là [0-9]foo).


2

Dựa trên tập lệnh đếm , câu trả lời của Shawn và mẹo Bash để đảm bảo ngay cả tên tệp có dòng mới được in ở dạng có thể sử dụng trên một dòng:

for f in *
do
    if [ -d "./$f" ]
    then
        printf %q "$f"
        printf %s ' '
        find "$f" -maxdepth 1 -printf x | wc -c
    fi
done

printf %qlà để in một phiên bản được trích dẫn của một chuỗi, nghĩa là một chuỗi đơn mà bạn có thể đặt vào một tập lệnh Bash để được hiểu là một chuỗi bằng chữ bao gồm các dòng mới (có khả năng) và các ký tự đặc biệt khác. Ví dụ, xem echo -n $'\tfoo\nbar'vs printf %q $'\tfoo\nbar'.

Các findlệnh hoạt động bằng cách đơn giản là in một nhân vật duy nhất cho mỗi tập tin, và sau đó đếm những người thay vì dòng đếm.


1

Dưới đây là một "brute-force" cách -ish để có được kết quả của bạn, sử dụng find, echo, ls, wc, xargsawk.

find . -maxdepth 1 -type d -exec sh -c "echo '{}'; ls -1 '{}' | wc -l" \; | xargs -n 2 | awk '{print $1" "$2}'

Công việc này. Nhưng đầu ra bị rối nếu bạn có các thư mục có `` khoảng trống trong tên.
ShyBoy

Điều này sẽ báo cáo kết quả không chính xác nếu bất kỳ tên tệp nào có ký tự dòng mới trong đó.
Shawn J. Goff

-1
for i in *; do echo $i; ls $i | wc -l; done

4
Chào mừng bạn đến với U & L. Câu trả lời phải ở dạng dài với phần giải thích và không chỉ đơn giản là mã giảm. Vui lòng mở rộng điều này và giải thích những gì đang xảy ra. Ngoài ra, đây là một cách rất không hiệu quả để làm điều này và không xử lý các tệp có khoảng trắng chẳng hạn.
slm

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.