Đệ quy tìm kiếm các tệp có phần mở rộng cụ thể


437

Tôi đang cố gắng tìm tất cả các tệp có phần mở rộng cụ thể trong một thư mục và các thư mục con của nó bằng bash của tôi (Bản phát hành Ubuntu LTS mới nhất).

Đây là những gì được viết trong một tập tin kịch bản:

#!/bin/bash

directory="/home/flip/Desktop"
suffix="in"

browsefolders ()
  for i in "$1"/*; 
  do
    echo "dir :$directory"
    echo "filename: $i"
    #   echo ${i#*.}
    extension=`echo "$i" | cut -d'.' -f2`
    echo "Erweiterung $extension"
    if     [ -f "$i" ]; then        

        if [ $extension == $suffix ]; then
            echo "$i ends with $in"

        else
            echo "$i does NOT end with $in"
        fi
    elif [ -d "$i" ]; then  
    browsefolders "$i"
    fi
  done
}
browsefolders  "$directory"

Thật không may, khi tôi bắt đầu tập lệnh này trong terminal, nó nói:

[: 29: in: unexpected operator

( $extensionthay vì 'in')

Chuyện gì đang xảy ra ở đây, lỗi ở đâu? Nhưng cái nẹp xoăn này


2
Lỗi là do thiếu '{'
shrewmouse

Câu trả lời:


749
find $directory -type f -name "*.in"

ngắn hơn một chút so với toàn bộ điều đó (và an toàn hơn - liên quan đến khoảng trắng trong tên tệp và tên thư mục).

Kịch bản của bạn có thể thất bại đối với các mục không có .tên của chúng, làm $extensiontrống.


16
vâng, findđược đệ quy theo mặc định. bạn có thể giới hạn độ sâu nếu muốn (xem trang hướng dẫn).
Mat

1
Tôi muốn chuyển tất cả các tệp được tìm thấy dưới dạng đối số sang tệp jar. Làm thế nào điều này có thể được thực hiện?
lật

8
@flip: đó là một câu hỏi khác nhau. Đăng một câu hỏi mới, nêu chi tiết chính xác những gì bạn muốn làm và những gì bạn đã cố gắng cho đến nay.
Mat

Một chút chỉnh sửa: sử dụng '* .in' hoặc \ *. Thay vì "* .in" vì dấu ngoặc kép không ngăn cản sự mở rộng vỏ. Tức là tập lệnh của bạn sẽ không hoạt động đúng nếu có tệp có phần mở rộng .in trong thư mục hiện tại.
Shnatsel

4
@Shnatsel: dấu ngoặc kép không ngăn cản sự mở rộng vỏ. Hãy thử nó.
Mat

188
find {directory} -type f -name '*.extension'

Ví dụ: Để tìm tất cả csvcác tệp trong thư mục hiện tại và các thư mục con của nó, hãy sử dụng:

find . -type f -name '*.csv'

60

Cú pháp tôi sử dụng hơi khác so với những gì @Matt đề xuất:

find $directory -type f -name \*.in

(đó là một lần gõ phím ít hơn).


1
Kịch bản của Matt cũng sẽ không hoạt động nếu có tệp có phần mở rộng .in trong thư mục hiện tại, trong khi tệp của bạn vẫn hoạt động. Xem stackoverflow.com/questions/5927369/
Mạnh

4
@Shnatsel nhận xét này (và do đó là của bạn) hoàn toàn sai.
gniourf_gniourf

1
@gniourf_gniourf Bạn nên cung cấp một số tài liệu tham khảo cho tuyên bố của mình, nếu không, người ta có thể đơn giản lập luận: "Không, bạn đã sai". Nhưng trên thực tế, bạn đã đúng: gnu.org/software/bash/manual/html_node/Double-Quotes.html
Murmel

@ user1885518: Tôi nghĩ rằng đó phải là người tuyên bố rằng tập lệnh không hoạt động, người sẽ cung cấp một số ví dụ khi tập lệnh thất bại. Đó là những gì tôi làm khi tôi để lại bình luận ở nơi có các đoạn script bị hỏng: thường là về các trích dẫn và tên tệp có chứa khoảng trắng, dòng mới, chuỗi, v.v., và tôi giải thích cụ thể tại sao nó bị hỏng.
gniourf_gniourf

2
Cung cấp tài liệu tham khảo luôn là một cách tốt trong một cuộc thảo luận, nó không phụ thuộc vào ai là người đầu tiên. Anh ấy nên, bạn nên.
Murmel

14

Không sử dụng find:

du -a $directory | awk '{print $2}' | grep '\.in$'

3
Điều grepnày không thực sự cần thiết ở đây. awkcó các biểu thức chính quy và có thể giới hạn đầu ra của nó thành các giá trị khớp với một mẫu.
Kenster

Phương pháp này cực kỳ hữu ích nếu bạn trải qua 100 giây terabyte. Lệnh find mất quá nhiều thời gian để xử lý. Điều này bắt đầu ngay lập tức.
Protonova

1
awk|greplà một mô hình chống. Hãy để awk làm grepping.
Jens

10
  1. Có một {mất tích saubrowsefolders ()
  2. Tất cả $innên$suffix
  3. Các dòng với cutbạn chỉ có phần giữa của front.middle.extension. Bạn nên đọc hướng dẫn sử dụng shell của bạn ${varname%%pattern}và bạn bè.

Tôi giả sử bạn làm điều này như một bài tập trong shell scripting, nếu không thì findgiải pháp đã được đề xuất là cách để đi.

Để kiểm tra cú pháp shell thích hợp, không chạy tập lệnh, hãy sử dụng sh -n scriptname.



7

Mặc dù sử dụng findlệnh có thể hữu ích ở đây, bản thân shell cung cấp các tùy chọn để đạt được yêu cầu này mà không cần bất kỳ công cụ của bên thứ ba nào. Các bashvỏ cung cấp một tùy chọn hỗ trợ glob mở rộng sử dụng mà bạn có thể lấy tên tập tin dưới đường đệ quy mà phù hợp với các phần mở rộng mà bạn muốn.

Tùy chọn mở rộng là tùy chọn extglobcần được đặt bằng cách sử dụng shopttùy chọn như dưới đây. Các tùy chọn được kích hoạt với sự -shỗ trợ và vô hiệu hóa với -ucờ anh ta . Ngoài ra, bạn có thể sử dụng một vài tùy chọn hơn, tức là nullglobtrong đó một quả cầu chưa từng có bị cuốn đi hoàn toàn, được thay thế bằng một tập hợp các từ không. Và globstarđiều đó cho phép lặp lại qua tất cả các thư mục

shopt -s extglob nullglob globstar

Bây giờ tất cả những gì bạn cần làm là hình thành biểu thức toàn cầu để bao gồm các tệp của một phần mở rộng nhất định mà bạn có thể làm như dưới đây. Chúng tôi sử dụng một mảng để điền vào kết quả toàn cầu vì khi được trích dẫn chính xác và mở rộng, tên tệp có ký tự đặc biệt sẽ vẫn còn nguyên và không bị hỏng do tách từ.

Ví dụ: để liệt kê tất cả các *.csvtệp trong đường dẫn đệ quy

fileList=(**/*.csv)

Tùy chọn **là lặp lại thông qua các thư mục con và *.csvđược mở rộng toàn cầu để bao gồm bất kỳ tệp nào của các tiện ích mở rộng được đề cập. Bây giờ để in các tập tin thực tế, chỉ cần làm

printf '%s\n' "${fileList[@]}"

Sử dụng một mảng và thực hiện một mở rộng được trích dẫn thích hợp là cách phù hợp khi được sử dụng trong các tập lệnh shell, nhưng để sử dụng tương tác, bạn có thể chỉ cần sử dụng lsvới biểu thức toàn cầu như

ls -1 -- **/*.csv

Điều này rất có thể được mở rộng để phù hợp với nhiều tệp tức là tệp kết thúc bằng nhiều phần mở rộng (nghĩa là tương tự như thêm nhiều cờ trong findlệnh). Ví dụ xem xét một trường hợp cần để có được tất cả các file hình ảnh đệ quy tức là các phần mở rộng *.gif, *.png*.jpg, tất cả các bạn cần phải là

ls -1 -- **/+(*.jpg|*.gif|*.png)

Điều này rất có thể được mở rộng để có kết quả phủ định cũng có. Với cùng một cú pháp, người ta có thể sử dụng kết quả của toàn cầu để loại trừ các tệp thuộc loại nhất định. Giả sử bạn muốn loại trừ tên tệp với các phần mở rộng ở trên, bạn có thể làm

excludeResults=()
excludeResults=(**/!(*.jpg|*.gif|*.png))
printf '%s\n' "${excludeResults[@]}"

Cấu trúc !()này là một hoạt động phủ định để không bao gồm bất kỳ phần mở rộng tệp nào được liệt kê bên trong và |là một toán tử xen kẽ giống như được sử dụng trong thư viện Biểu thức chính quy mở rộng để thực hiện khớp OR của các khối.

Lưu ý rằng các hỗ trợ toàn cầu mở rộng này không có sẵn trong vỏ bourne POSIX và hoàn toàn cụ thể cho các phiên bản gần đây của bash. Vì vậy, nếu bạn đang xem xét tính di động của các tập lệnh chạy trên POSIX và bashshell, tùy chọn này sẽ không đúng.


6

Để tìm tất cả các pom.xmltệp trong thư mục hiện tại của bạn và in chúng, bạn có thể sử dụng:

find . -name 'pom.xml' -print

1
find $directory -type f -name "*.in"|grep $substring

0
for file in "${LOCATION_VAR}"/*.zip
do
  echo "$file"
done 

1
Mặc dù mã này có thể trả lời câu hỏi, cung cấp ngữ cảnh bổ sung về lý do và / hoặc cách mã này trả lời câu hỏi cải thiện giá trị lâu dài của nó.
rollstuhlfahrer
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.