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 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
Câu trả lời:
Đ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 $f
chứa tên tệp -test
, find
sẽ 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 $f
chứ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,
và 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 find
lệ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.
-mindepth 1
-printf '\n'
thay vì -exec echo
.
-printf
, nhưng không phải nếu bạn muốn nó hoạt động trên FreeBSD chẳng hạn.
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 -maxdepth
1 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 đó.
find . -maxdepth 1 -type d
đầu ra?
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 */ ...
-exec echo
lệ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.
Bởi không có đệ quy, bạn có nghĩa là nếu directoryName1
có 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 -f
kiể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ế -f
bằ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
).
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 %q
là để 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 find
lệ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.
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
, xargs
và awk
.
find . -maxdepth 1 -type d -exec sh -c "echo '{}'; ls -1 '{}' | wc -l" \; | xargs -n 2 | awk '{print $1" "$2}'
for i in *; do echo $i; ls $i | wc -l; done
for i in `ls -1`; do echo $i : `ls -1 $i|wc -l`; done
find
khi 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ừ.
và..
)