Có một sự thay thế ngắn gọn hơn cho đường ống đến wc để đếm các tập tin trong một thư mục


12

Nếu tôi làm ls -1 target_dir | wc -l, tôi nhận được một số lượng các tập tin trong một thư mục. Tôi thấy điều này hơi cồng kềnh. Có một cách thanh lịch hoặc cô đọng hơn?


2
Bạn không cần "-1" khi đường ống đến wc.
Steve

lsđã cho tổng số, vậy làm thế nào về ls -l | head -1? Làm cho nó một bí danh nếu bạn muốn một cái gì đó ngắn hơn.
Daniel Wagner

2
@DanielWagner Đầu ra "Total: nnn" bằng cách ls -lchỉ ra tổng kích thước của các tệp, không phải số lượng tệp.
David Richerby

2
Hãy nhớ rằng ls | wc -lsẽ cung cấp cho bạn số đếm sai nếu bất kỳ tên tệp nào chứa dòng mới.
chepner

Điều này phụ thuộc vào hệ thống tệp và đếm các thư mục + 2 trong một thư mục. Câu trả lời có thêm 2 (vì nó tự đếm và cha mẹ của nó). stat -c %h .cung cấp thông tin tương tự nhưls -ld . | cut -d" " -f 2
ctrl-alt-delor

Câu trả lời:


12

Giả sử bash 4+ (mà bất kỳ phiên bản Ubuntu được hỗ trợ nào):

num_files() (
    shopt -s nullglob
    cd -P -- "${1-.}" || return
    set -- *
    echo "$#"
)

Gọi nó là num_files [dir]. dirlà tùy chọn, nếu không nó sử dụng thư mục hiện tại. Phiên bản gốc của bạn không tính các tập tin ẩn, vì vậy cũng không có. Nếu bạn muốn điều đó, shopt -s dotglobtrước đây set -- *.

Ví dụ ban đầu của bạn không chỉ đếm các tệp thông thường, mà cả các thư mục và các thiết bị khác - nếu bạn thực sự chỉ muốn các tệp thông thường (bao gồm cả các liên kết tượng trưng đến các tệp thông thường), bạn sẽ cần kiểm tra chúng:

num_files() (
    local count=0

    shopt -s nullglob
    cd -P -- "${1-.}" || return
    for file in *; do
        [[ -f $file ]] && let count++
    done
    echo "$count"
)

Nếu bạn có GNU find, một cái gì đó như thế này cũng là một tùy chọn (lưu ý rằng điều này bao gồm các tệp ẩn, điều mà lệnh ban đầu của bạn không làm được):

num_files() {
    find "${1-.}" -maxdepth 1 -type f -printf x | wc -c
}

(thay đổi -typethành -xtypenếu bạn cũng muốn đếm liên kết tượng trưng cho các tệp thông thường).


Sẽ không setthất bại nếu có rất nhiều tập tin? Tôi nghĩ rằng bạn có thể phải sử dụng xargsvà một số mã tổng hợp để làm cho nó hoạt động trong trường hợp chung.
l0b0

1
Ngoài ra shopt -s dotglobnếu bạn muốn các tệp bắt đầu .được tính
Chấn thương kỹ thuật số

1
@ l0b0 Tôi không nghĩ setsẽ thất bại trong những trường hợp này, vì chúng ta không thực sự làm exec. Nói một cách dí dỏm, trên hệ thống của tôi, getconf ARG_MAXmang lại 262144, nhưng nếu tôi làm test_arg_max() { set -- {1..262145}; echo $#; }; test_arg_max, nó vui vẻ trả lời 262145.
kojiro

@DavidR Richby -maxdepthkhông phải là POSIX.
Chris Down

4
@MichaelMartinez Viết mã rõ ràng không phải là sự thay thế cho việc viết mã chính xác.
Chris Xuống

3

f=(target_dir/*);echo ${#f[*]}

hoạt động chính xác cho tập tin với không gian, dòng mới, vv trong tên.


bạn có thể cung cấp một số bối cảnh? Điều này nên đi trong một kịch bản bash?
codecowboy

Nó có thể. bạn cũng có thể đặt nó trực tiếp vào vỏ. phiên bản đó giả định bạn muốn thư mục hiện tại; Tôi đã chỉnh sửa nó để nó gần với câu hỏi của bạn hơn. về cơ bản, nó tạo ra một biến mảng shell chứa tất cả các tệp trong thư mục, sau đó in số đếm của mảng đó. nên hoạt động trong bất kỳ shell nào với mảng - bash, ksh, zsh, v.v. - nhưng có lẽ không phải là sh / ash / dash đơn giản.
Aaron Davies

2

lslà nhiều cột chỉ khi nó xuất trực tiếp ra thiết bị đầu cuối, bạn có thể xóa tùy chọn "-1", Bạn có thể xóa wctùy chọn "-l", chỉ đọc giá trị đầu tiên (giải pháp lười biếng, không được sử dụng cho bằng chứng hợp pháp, điều tra hình sự, nhiệm vụ quan trọng, chiến thuật ops ..).

ls target | wc 

5
Điều này thất bại cho tên tập tin có chứa dòng mới.
l0b0

@Emmanuel Bạn sẽ cần phân tích kết quả của bạn wcđể có được số lượng tệp trong trường hợp không quan trọng, vậy làm thế nào đây thậm chí là một giải pháp?
l0b0

@Emmanuel Điều này có thể thất bại nếu targetlà một quả địa cầu, khi được mở rộng, bao gồm một số thứ bắt đầu bằng dấu gạch nối. Ví dụ: tạo một thư mục mới, truy cập vào đó và thực hiện touch -- {1,2,3,-a}.txt && ls *|wc(NB: sử dụng rm -- *.txtđể xóa các tệp đó.)
David Richerby

Ý bạn là wc -lsao Nếu không, bạn nhận được số dòng mới, từ và byte của lsđầu ra. Đó là những gì David Richerby nói: Bạn phải phân tích lại nó.
erik

@erik Tôi wckhông có đối số mà bạn không cần phân tích nếu bộ não của bạn biết rằng đối số đầu tiên là dòng mới.
Emmanuel

2

Nếu đó là sự ngắn gọn mà bạn theo đuổi (chứ không phải là chính xác khi xử lý các tệp có dòng mới trong tên của họ, v.v.), tôi khuyên bạn chỉ nên đặt bí danh wc -lcho lc("đếm số dòng"):

$ alias lc='wc -l'
$ ls target_dir|lc

Như những người khác đã lưu ý, bạn không cần -1tùy chọn này ls, vì nó tự động khi lsghi vào đường ống. (Trừ khi bạn cóls bí danh để luôn sử dụng chế độ cột. Tôi đã thấy điều đó trước đây, nhưng không thường xuyên lắm.)

Một lcbí danh nói chung khá tiện dụng và đối với câu hỏi này, nếu bạn nhìn vào trường hợp "đếm thư mục hiện tại", ls|lcthì gần như cô đọng như bạn có thể nhận được.


2

Cho đến nay Aaron là cách tiếp cận duy nhất cô đọng hơn của bạn. Một phiên bản chính xác hơn của phương pháp của bạn có thể trông giống như:

ls -aR1q | grep -Ecv '^\./|/$|^$'

Điều đó liệt kê đệ quy tất cả các tệp - không phải thư mục - một thư mục trên mỗi dòng, bao gồm các tệp .dotf bên dưới thư mục hiện tại bằng cách sử dụng các ký tự shell khi cần thiết để thay thế các ký tự không in được. grep lọc ra bất kỳ danh sách thư mục mẹ hoặc .. hoặc * / hoặc dòng trống - vì vậy chỉ nên có một dòng trên mỗi tệp - tổng số grep trả về cho bạn. Nếu bạn muốn bao gồm các thư mục con, hãy làm:

ls -aR1q | grep -Ecv '^\.{1,2}/|^$'

Xóa -Rtrong cả hai trường hợp nếu bạn không muốn kết quả đệ quy.


1
Tôi có xu hướng thích làm điều đó với find. Nếu bạn chỉ muốn đếm, điều này sẽ hoạt động: find -mindepth 1 -maxdepth 1 -printf '\n'|wc -l(loại bỏ các điều khiển độ sâu để có kết quả đệ quy).
Aaron Davies

@AaronDavies - điều đó không thực sự hiệu quả. Đặt một dòng mới trong bất kỳ tên tập tin nào và tự mình xem. Ngoài ra, để làm điều tương tự một cách hợp lý bạn làm: find . \! -name . -prune | wc -l- tất nhiên vẫn không hoạt động.
mikeerv

1
Tôi không tuân theo - printfhướng dẫn in một chuỗi không đổi (một dòng mới) hoàn toàn không bao gồm tên tệp, vì vậy kết quả không phụ thuộc vào bất kỳ tên tệp lạ nào. Thủ thuật này hoàn toàn không thể thực hiện được với một findđiều không hỗ trợ printf.
Aaron Davies

@AaronDavies - ồ, đúng rồi. Tôi giả sử tên tập tin được bao gồm. Tất nhiên, nó có thể được thực hiện một cách hợp lý:find .//. \!. -name . -prune | grep -c '^\.//\.'
mikeerv

xuất sắc! /là nhân vật duy nhất không thể xuất hiện trong tên tệp, .//.trình tự được đảm bảo xuất hiện chính xác một lần cho mỗi tệp, phải không? một vài câu hỏi mặc dù - tại sao .//., và tại sao -prune? khi nào thì điều này khác với find . \! -name . | grep -c '^\.'? (tôi cho rằng .trong bạn \!.là một lỗi đánh máy.)
Aaron Davies
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.