Lặp qua tất cả các tệp với một phần mở rộng cụ thể


109
for i in $(ls);do
    if [ $i = '*.java' ];then
        echo "I do something with the file $i"
    fi
done

Tôi muốn lặp lại từng tệp trong thư mục hiện tại và kiểm tra xem nó có khớp với một phần mở rộng cụ thể hay không. Đoạn mã trên không hoạt động, bạn có biết tại sao không?


4
Về for i in $(ls *.java); do echo "do something with file $i"; donethì sao?
speakr

không có cách nào để sửa chữa câu lệnh if đó?
AR89

1
Bạn đang so sánh $ivới chuỗi chữ "* .java"; mở rộng mẫu không được thực hiện ở đây.
chepner

Để sửa chữa câu lệnh if như bạn có, hãy sử dụng if [[ $i == *.java ]]; then.. (lưu ý dấu kép [[]] và không được trích dẫn * .java).
anh chàng khác

2
Đừng phân tích cú phápls - chấp nhận câu trả lời @ chepner của
glenn Jackman

Câu trả lời:


201

Không cần thủ thuật ưa thích:

for i in *.java; do
    [ -f "$i" ] || break
    ...
done

Người bảo vệ đảm bảo rằng nếu không có tệp nào phù hợp, vòng lặp sẽ thoát ra mà không cần cố gắng xử lý tên tệp không tồn tại *.java. Trong bash(hoặc các trình bao hỗ trợ một cái gì đó tương tự), bạn có thể sử dụng nullglobtùy chọn để đơn giản bỏ qua một kết quả không kết hợp và không nhập nội dung của vòng lặp.

shopt -s nullglob
for i in *.java; do
    ...
done

5
Cách đơn giản nhất là thêm mẫu khác: for i in *.java *.cpp; do. Nếu bạn đã bật các mẫu mở rộng bashvới shopt -s extglob, bạn có thể viết for i in *.@(java|cpp); do.
chepner

8
Nó sẽ xảy ra nếu nó thực sự khớp với bất kỳ tệp nào. Bạn cần sử dụng shopt -s nullglobđể một mẫu không phù hợp mở rộng thành chuỗi trống thay vì được xử lý theo nghĩa đen.
chepner

1
@zygimantus vâng, điều đó nên xảy ra, miễn là thư mục hiện tại là nơi tập lệnh đang chạy. Nếu bạn đang không ở trong thư mục bạn muốn trở thành mặc dù, bạn nên cdvào thư mục đó trước khi bắt đầu forvòng lặp
danielsdesk

1
@puk Nó phụ thuộc vào các tùy chọn shell của bạn. Theo mặc định, một mẫu không khớp được coi là một chuỗi ký tự. Bạn có thể đặt nullglobđể nó được coi là một chuỗi trống hoặc đặt failglobđể nó được coi là một lỗi. (Nếu bạn đặt cả hai, failglobsẽ được ưu tiên hơn.)
chepner

3
@chepner Có thể hữu ích cho những người không phải là chuyên gia nếu điều này được chỉ ra trong câu trả lời vì ai đó có thể sao chép, dán nó và thấy nó không hoạt động. Một ví dụ hoàn hảo sẽ là ai đó đang chuyển đổi tất cả các tệp ".jpg" thành " .png" trước khi thực hiện một chức năng quan trọng
puk

13

câu trả lời đúng là @ chepner

EXT=java
for i in *.${EXT}; do
    ...
done

tuy nhiên, đây là một mẹo nhỏ để kiểm tra xem tên tệp có phần mở rộng nhất định hay không:

EXT=java
for i in *; do
    if [ "${i}" != "${i%.${EXT}}" ];then
        echo "I do something with the file $i"
    fi
done

nó sẽ như thế nào với một biến extthay vì .java?
AR89,

13

Thêm đệ quy các thư mục con,

for i in `find . -name "*.java" -type f`; do
    echo "$i"
done

thay vì find . -name "*.java" -type f -exec echo \{\} \; để tránh misparsing của đầu ra củafind
umläute

1
Và nếu bạn không, ít nhất bạn nên trích dẫn "$i"bên trong vòng lặp.
tripleee

2
Điều này sẽ không hoạt động nếu bất kỳ tệp nào có khoảng trắng trong tên của chúng.
codeforester

1
@codeforester Tôi gặp chính xác vấn đề đó và tôi đã khắc phục nó bằng phương pháp từ câu trả lời này: askubuntu.com/a/343753/551184
fsinisi90

7

Vòng qua tất cả các file có đuôi: .img,.bin , .txthậu tố, và in tên tập tin:

for i in *.img *.bin *.txt;
do
  echo "$i"
done

Hoặc theo cách đệ quy (cũng tìm thấy trong tất cả các thư mục con):

for i in `find . -type f -name "*.img" -o -name "*.bin" -o -name "*.txt"`;
do
  echo "$i"
done

3

Tôi đồng ý với các câu trả lời khác về cách chính xác để lặp lại các tệp. Tuy nhiên OP yêu cầu:

Đoạn mã trên không hoạt động, bạn có biết tại sao không?

Đúng!

Một bài báo xuất sắc Sự khác biệt giữa thử nghiệm là gì, [và [[?] Giải thích chi tiết rằng trong số những sự khác biệt khác, bạn không thể sử dụng expression matchinghoặc pattern matchingtrong testlệnh (viết tắt của [)

Tính năng thử nghiệm mới [[thử nghiệm cũ [Ví dụ

Đối sánh mẫu = (hoặc ==) (không khả dụng) [[$ name = a *]] || echo "tên không bắt đầu bằng 'a': $ name"

Biểu thức chính quy = ~ (không khả dụng) [[$ (date) = ~ ^ T6 \ ... \ 13]] && echo "Hôm nay là Thứ Sáu ngày 13!"
phù hợp

Vì vậy, đây là nguyên nhân khiến script của bạn bị lỗi. Nếu OP quan tâm đến câu trả lời có [[cú pháp (có nhược điểm là không được hỗ trợ trên nhiều nền tảng như [lệnh), tôi sẽ sẵn lòng chỉnh sửa câu trả lời của mình để đưa vào.

CHỈNH SỬA: Mọi lời khuyên về cách định dạng dữ liệu trong câu trả lời dưới dạng bảng sẽ hữu ích!


2

như @chepner nói trong nhận xét của mình, bạn đang so sánh $ i với một chuỗi cố định.

Để mở rộng và khắc phục tình hình, bạn nên sử dụng [[]] với toán tử regex = ~

ví dụ:

for i in $(ls);do
    if [[ $i =~ .*\.java$ ]];then
        echo "I want to do something with the file $i"
    fi
done

regex ở bên phải của = ~ được kiểm tra so với giá trị của toán tử bên trái và không nên được trích dẫn, (được trích dẫn sẽ không bị lỗi nhưng sẽ so sánh với một chuỗi cố định và vì vậy rất có thể sẽ không thành công "

nhưng câu trả lời của @chepner ở trên bằng cách sử dụng cầu là một cơ chế hiệu quả hơn nhiều.


nó sẽ như thế nào với một biến extthay vì .java?
AR89,

1
ack, không cần biểu thức chính quy: if [[ $i == *.java ]]hoặc if [[ $i == *.$ext ]]. Nhưng làm ls không phân tích cú pháp
glenn Jackman

1

Tôi thấy giải pháp này khá tiện dụng. Nó sử dụng -ortùy chọn trong find:

find . -name \*.tex -or -name "*.png" -or -name "*.pdf"

Nó sẽ tìm các tập tin với phần mở rộng tex, pngpdf.

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.