Điều gì mở rộng cho tất cả các tệp trong thư mục hiện tại một cách đệ quy?


91

Tôi biết **/*.extmở rộng cho tất cả các tệp trong tất cả các thư mục con phù hợp *.ext, nhưng một bản mở rộng tương tự bao gồm tất cả các tệp như vậy trong thư mục hiện tại là gì?


4
Bash của tôi không xử lý **/*.ext. Bạn có chắc nó làm việc cho bạn?
tangens

@tangens Bạn phải bật globstartùy chọn theo câu trả lời của Dennis.
kenorb

Câu trả lời:


110

Điều này sẽ hoạt động trong Bash 4:

ls -l {,**/}*.ext

Để dấu sao kép hoạt động, bạn globstarcần đặt tùy chọn (mặc định: bật):

shopt -s globstar

Từ man bash :

    sao cầu
                  Nếu được đặt, mẫu ** được sử dụng trong con đường mở rộng tên tệp‐
                  văn bản sẽ khớp với một tệp và không hoặc nhiều thư mục và
                  các thư mục con. Nếu mẫu được theo sau bởi dấu /, chỉ
                  thư mục và thư mục con khớp nhau.

Bây giờ tôi đang tự hỏi liệu có thể đã từng có lỗi trong quá trình xử lý hình cầu không, bởi vì bây giờ sử dụng đơn giản là ls **/*.exttôi nhận được kết quả chính xác.

Bất chấp điều đó, tôi đã xem xét phân tích mà kenorb đã thực hiện bằng cách sử dụng kho VLC và tìm thấy một số vấn đề với phân tích đó và trong câu trả lời của tôi ngay lập tức ở trên:

Các so sánh với đầu ra của findlệnh không hợp lệ vì việc chỉ định -type fkhông bao gồm các loại tệp khác (cụ thể là các thư mục) và các lslệnh được liệt kê có khả năng làm được. Ngoài ra, một trong các lệnh được liệt kê, ls -1 {,**/}*.*- dường như dựa trên lệnh của tôi ở trên, chỉ xuất ra các tên bao gồm dấu chấm cho những tệp nằm trong thư mục con. Câu hỏi của OP và câu trả lời của tôi bao gồm một dấu chấm vì thứ đang được tìm kiếm là các tệp có phần mở rộng cụ thể.

Tuy nhiên, quan trọng nhất là có một vấn đề đặc biệt khi sử dụng lslệnh với mẫu hình sao cầu **. Nhiều bản sao phát sinh do mẫu được Bash mở rộng thành tất cả các tên tệp (và tên thư mục) trong cây đang được kiểm tra. Sau phần mở rộng, lslệnh liệt kê từng người trong số họ và nội dung của chúng nếu chúng là thư mục.

Thí dụ:

Trong thư mục hiện tại của chúng tôi là thư mục con Avà nội dung của nó:

A
└── AB
    └── ABC
        ├── ABC1
        ├── ABC2
        └── ABCD
            └── ABCD1

Trong cây đó, **mở rộng thành "AA / AB A / AB / ABC A / AB / ABC / ABC1 A / AB / ABC / ABC2 A / AB / ABC / ABCD A / AB / ABC / ABCD / ABCD1" (7 mục) . Nếu bạn làm như vậy thì echo **đó là đầu ra chính xác mà bạn nhận được và mỗi mục nhập được thể hiện một lần. Tuy nhiên , nếu bạn làm vậy, ls **nó sẽ xuất ra một danh sách của từng mục nhập đó. Vì vậy, về cơ bản nó được ls Atheo sau bởi ls A/AB, v.v., vì vậy A/ABđược hiển thị hai lần. Ngoài ra, lssẽ đặt đầu ra của từng thư mục con riêng biệt:

...
<blank line>
directory name:
content-item
content-item

Vì vậy, sử dụng wc -lđếm tất cả các dòng trống đó và tiêu đề phần tên thư mục sẽ đẩy số lượng ra xa hơn.

Đây là một lý do khác tại sao bạn không nên phân tích cú phápls .

Do kết quả của phân tích sâu hơn này, tôi khuyên bạn không nên sử dụng mẫu hình sao cầu trong bất kỳ trường hợp nào ngoài việc lặp lại một cây tệp theo cách sau:

for entry in **
do
    something "$entry"
done

Để so sánh cuối cùng, tôi đã sử dụng kho lưu trữ mã nguồn Bash mà tôi có sẵn và thực hiện điều này:

shopt -s globstar dotglob
diff <(echo ** | tr ' ' '\n') <(find . | sed 's|\./||' | sort)
0a1
> .

Tôi đã sử dụng trđể thay đổi dấu cách thành dòng mới chỉ hợp lệ ở đây vì không có tên nào bao gồm dấu cách. Tôi đã sử dụng sedđể loại bỏ hàng đầu ./từ mỗi dòng đầu ra từ find. Tôi đã sắp xếp đầu ra findvì nó thường không được sắp xếp và việc mở rộng các quả địa cầu của Bash đã được sắp xếp. Như bạn có thể thấy, đầu ra duy nhất từ diffthư mục hiện tại là .đầu ra của find. Khi tôi đã làmls ** | wc -l , đầu ra có gần gấp đôi số dòng.


5
Tôi đã thử nghiệm Ubuntu và Cygwin, và globstarđược mặc địnhoff
Steven Penny

12
Câu trả lời tốt nhất! nhưng tôi nghĩ rằng **/*.extphải là đủ mặc dù. Ngoài ra, bạn sẽ không có các tệp ẩn trừ khi bạn shopt -s dotglob.
gniourf_gniourf

2
Để vô hiệu hóa globstar: shopt -u globstar.
kenorb

4
@gniourf_gniourf Câu hỏi đặt ra thực sự yêu cầu để bao gồm các thư mục hiện hành đặc biệt vì vậy không, **/*.extsẽ không đủ
Mściwoj

2
@dotnetCarpenter: Phiên bản Bash đi kèm với MacOS là 3.2, phiên bản này không hỗ trợ globalstar, như bạn đã phát hiện. Dấu hoa thị kép được coi giống như dấu hoa thị đơn. Globstar đã được giới thiệu trong Bash 4.0.
Tạm dừng cho đến khi có thông báo mới.

13

Điều này sẽ in tất cả các tệp trong thư mục hiện tại và các thư mục con của nó kết thúc bằng '.ext'.

find . -name '*.ext' -print

Mặc dù câu trả lời này không đáp ứng được yêu cầu "mở rộng" theo nghĩa chặt chẽ nhất của OP, nhưng nó có nhiều khả năng tạo ra kết quả mong muốn.
Tạm dừng cho đến khi có thông báo mới.

7

Bạn có thể sử dụng: **/*.*để bao gồm tất cả các tệp một cách đệ quy (bật bằng cách shopt -s globstar:).

Vui lòng xem bên dưới thử nghiệm các biến thể khác và cách chúng hoạt động.


Thư mục thử nghiệm với 3472 tệp trong thư mục kho lưu trữ VLC mẫu :

(Tổng số 3472 tệp được tính theo find . -type f | wc -l:)

  • ls -1 **/*.* - trả về 3338
  • ls -1 {,**/}*.*- trả về 3341 (theo đề xuất của Dennis )
  • ls -1 {,**/}* - trả về 8265
  • ls -1 **/*- trả về 7817, ngoại trừ các tệp ẩn (theo đề xuất của Dennis )
  • ls -1 **/{.[^.],}*- trả về 7869 (theo đề xuất của Dennis )
  • ls -1 {,**/}.?* - trả lại 15855
  • ls -1 {,**/}.* - trả về 20321

Vì vậy, tôi nghĩ rằng phương pháp gần nhất để liệt kê tất cả các tệp một cách đệ quy là ví dụ đầu tiên ( **/*.*) theo nhận xét của gniourf-gniourf (giả sử các tệp có phần mở rộng thích hợp hoặc sử dụng phần mở rộng cụ thể), vì ví dụ thứ hai cung cấp thêm một số bản sao như bên dưới :

$ diff -u <(ls -1 {,**/}*.*) <(ls -1 **/*.*)
--- /dev/fd/63  2015-04-19 15:25:07.000000000 +0100
+++ /dev/fd/62  2015-04-19 15:25:07.000000000 +0100
@@ -1,6 +1,4 @@
 COPYING.LIB
-COPYING.LIB
-Makefile.am
 Makefile.am
@@ -45,7 +43,6 @@
 compat/tdestroy.c
 compat/vasprintf.c
 configure.ac
-configure.ac

và cái kia tạo ra nhiều bản sao hơn nữa.


Để bao gồm các tệp ẩn, hãy sử dụng: shopt -s dotglob(tắt bằng cách shopt -u dotglob). Nó không được khuyến khích, vì nó có thể ảnh hưởng đến các lệnh như mvhoặc rmvà bạn có thể vô tình xóa các tệp sai.


Trên thiết bị đầu cuối Mac và bash có bật sao cầu, tôi thấy giải pháp trên ( **/*.*) đầy đủ thông tin và hoạt động tốt nhất. Câu trả lời được chấp nhận gây ra trùng lặp các mục trong thư mục trên cùng. Mô hình làm việc của tôi là:"${path}"**/*.*
mummybot

Nó sẽ là thú vị để thử điều này với các tùy chọn khác như nullglob và dotglob
Wilf

3
$ find . -type f

Điều đó sẽ liệt kê tất cả các tệp trong thư mục hiện tại. Sau đó, bạn có thể thực hiện một số lệnh khác trên đầu ra bằng -exec

$find . -type f -exec grep "foo" {} \;

Điều đó sẽ gửi từng tệp từ tìm thấy cho chuỗi "foo".


Bây giờ đã 11 năm sau, có thể đã đến lúc ai đó chỉ ra rằng find . -type fáp dụng đệ quy với thư mục gốc tại thư mục hiện tại, không chỉ cho thư mục hiện tại.
Roger Dahl

3

Tại sao không chỉ sử dụng mở rộng dấu ngoặc nhọn để bao gồm cả thư mục hiện tại?

./{*,**/*}.ext

Mở rộng Brace xảy ra trước khi mở rộng toàn cầu, vì vậy bạn có thể thực hiện hiệu quả những gì bạn muốn với các phiên bản bash cũ hơn và có thể bỏ qua việc phát triển mạnh mẽ với cầu sao trong các phiên bản mới hơn.

Ngoài ra, nó được coi là một phương pháp hay trong bash để bao gồm phần dẫn đầu ./trong các mẫu hình cầu của bạn.

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.