วนซ้ำไฟล์ทั้งหมดที่มีนามสกุลเฉพาะ


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

ฉันต้องการวนซ้ำแต่ละไฟล์ในโฟลเดอร์ปัจจุบันและตรวจสอบว่าตรงกับนามสกุลที่ระบุหรือไม่ โค้ดด้านบนใช้ไม่ได้คุณรู้ไหมว่าทำไม?


4
เกี่ยวกับอะไรfor i in $(ls *.java); do echo "do something with file $i"; done?
พูด

ไม่มีทางที่จะแก้ไข if statement?
AR89

1
คุณกำลังเปรียบเทียบ$iกับสตริงตัวอักษร "* .java"; ไม่ได้ทำการขยายรูปแบบที่นี่
chepner

ในการแก้ไขคำสั่ง if ตามที่คุณมีให้ใช้if [[ $i == *.java ]]; then.. (สังเกต double [[]] s และ unquoted * .java)
ผู้ชายคนนั้น

2
อย่าแยกวิเคราะห์ls - ยอมรับคำตอบของ @ chepner
glenn jackman

คำตอบ:


202

ไม่จำเป็นต้องใช้เทคนิคแฟนซี:

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

*.javaสร้างความมั่นใจยามว่าถ้ามีไฟล์ที่ตรงกับที่วงจะออกโดยไม่ต้องพยายามในการประมวลผลชื่อไฟล์ที่ไม่มีอยู่จริง ในbash(หรือเปลือกหอยที่รองรับสิ่งที่คล้ายกัน) คุณสามารถใช้nullglobตัวเลือกเพื่อละเว้นการจับคู่ที่ล้มเหลวและไม่เข้าสู่เนื้อหาของลูป

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

5
for i in *.java *.cpp; doวิธีที่ง่ายที่สุดคือการเพิ่มรูปแบบอื่น: หากคุณมีการขยายรูปแบบการเปิดใช้งานในbashที่มีคุณสามารถเขียนshopt -s extglob for i in *.@(java|cpp); do
chepner

8
มันจะตรงกับไฟล์ใด ๆ จริงๆ คุณต้องใช้shopt -s nullglobเพื่อให้รูปแบบที่ไม่ตรงกันขยายไปยังลำดับว่างแทนที่จะใช้ตามตัวอักษร
chepner

1
@zygimantus ใช่มันควรตราบใดที่ไดเร็กทอรีปัจจุบันเป็นที่ที่สคริปต์กำลังทำงานอยู่ หากคุณไม่ได้อยู่ในไดเร็กทอรีที่คุณต้องการคุณควรcdไปที่ไดเร็กทอรีนั้นก่อนที่จะเริ่มforลูป
danielsdesk

1
@puk มันขึ้นอยู่กับตัวเลือกเชลล์ของคุณ ตามค่าเริ่มต้นรูปแบบที่ไม่ตรงกันจะถือว่าเป็นสตริงตามตัวอักษร คุณสามารถตั้งค่าnullglobให้ถือว่าเป็นลำดับว่างหรือตั้งค่าfailglobให้ถือว่าเป็นข้อผิดพลาด (หากคุณตั้งค่าทั้งสองfailglobจะมีความสำคัญกว่า)
chepner

3
@chepner อาจเป็นประโยชน์สำหรับผู้ที่ไม่ใช่ผู้เชี่ยวชาญในการระบุสิ่งนี้ในคำตอบเนื่องจากอาจมีคนคัดลอกวางและพบว่าไม่ได้ผล ตัวอย่างที่สมบูรณ์แบบคือคนที่แปลงไฟล์ " .jpg" ทั้งหมดเป็น " .png" ก่อนที่จะทำหน้าที่สำคัญ
puk

13

คำตอบที่ถูกต้องคือ @ chepner's

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

อย่างไรก็ตามนี่เป็นเคล็ดลับเล็ก ๆ ในการตรวจสอบว่าชื่อไฟล์มีนามสกุลที่กำหนดหรือไม่:

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

มันจะเป็นยังไงกับตัวแปรextแทน.java?
AR89

13

เพิ่มโฟลเดอร์ย่อยซ้ำ ๆ

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

แทนที่จะfind . -name "*.java" -type f -exec echo \{\} \; หลีกเลี่ยงการแยกวิเคราะห์ผลลัพธ์ของfind
umläute

1
และถ้าคุณไม่ทำอย่างน้อยคุณควรพูด"$i"ในวง
tripleee

2
การดำเนินการนี้จะใช้ไม่ได้หากไฟล์ใด ๆ มีช่องว่างในชื่อ
codeforester

1
@codeforester ฉันมีปัญหานั้นและฉันได้แก้ไขโดยใช้วิธีการจากคำตอบนี้: askubuntu.com/a/343753/551184
fsinisi90

7

ห่วงผ่านไฟล์ทั้งหมดที่ลงท้ายด้วย: .img, .bin, .txtต่อท้ายและพิมพ์ชื่อไฟล์:

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

หรือในลักษณะวนซ้ำ (ค้นหาในไดเร็กทอรีย่อยทั้งหมดด้วย):

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

3

ฉันเห็นด้วยกับคำตอบอื่น ๆ เกี่ยวกับวิธีการวนซ้ำไฟล์ที่ถูกต้อง อย่างไรก็ตาม OP ถามว่า:

โค้ดด้านบนใช้ไม่ได้คุณรู้ไหมว่าทำไม?

ใช่

บทความที่ยอดเยี่ยมอะไรคือความแตกต่างระหว่างการทดสอบ [และ [[?]]อธิบายรายละเอียดว่าในความแตกต่างอื่น ๆ คุณไม่สามารถใช้expression matchingหรือpattern matchingอยู่ในtestคำสั่ง (ซึ่งเป็นชวเลขสำหรับ[)

คุณลักษณะการทดสอบใหม่ [[แบบทดสอบเก่า [ตัวอย่าง

การจับคู่รูปแบบ = (หรือ ==) (ไม่มี) [[$ name = a *]] || echo "ชื่อไม่ได้ขึ้นต้นด้วย" a ": $ name"

นิพจน์ทั่วไป = ~ (ไม่มี) [[$ (date) = ~ ^ Fri \ ... \ 13]] && echo "มันคือวันศุกร์ที่ 13!"
การจับคู่

นี่คือสาเหตุที่สคริปต์ของคุณล้มเหลว หาก OP สนใจคำตอบเกี่ยวกับ[[ไวยากรณ์ (ซึ่งมีข้อเสียที่ไม่ได้รับการสนับสนุนบนแพลตฟอร์มมากเท่า[คำสั่ง) ฉันยินดีที่จะแก้ไขคำตอบของฉันเพื่อรวมไว้

แก้ไข: Protips ใด ๆ สำหรับวิธีจัดรูปแบบข้อมูลในคำตอบเป็นตารางจะเป็นประโยชน์!


2

ตามที่ @chepner กล่าวในความคิดเห็นของเขาว่าคุณกำลังเปรียบเทียบ $ i กับสตริงคงที่

ในการขยายและแก้ไขสถานการณ์คุณควรใช้ [[]] กับตัวดำเนินการ regex = ~

เช่น:

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

regex ทางด้านขวาของ = ~ จะถูกทดสอบกับค่าของตัวดำเนินการด้านซ้ายและไม่ควรยกมา (การยกมาจะไม่ผิดพลาด แต่จะเปรียบเทียบกับสตริงคงที่และมักจะล้มเหลว "

แต่คำตอบของ @chepner ข้างต้นโดยใช้ glob เป็นกลไกที่มีประสิทธิภาพมากกว่า


มันจะเป็นยังไงกับตัวแปรextแทน.java?
AR89

1
ack ไม่จำเป็นต้องใช้นิพจน์ทั่วไป: if [[ $i == *.java ]]หรือif [[ $i == *.$ext ]]. แต่อย่าแยกวิเคราะห์ ls
glenn jackman

1

ฉันพบว่าวิธีนี้มีประโยชน์มากทีเดียว ใช้-orตัวเลือกในfind:

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

มันจะหาไฟล์ที่มีนามสกุลtex, และpngpdf

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.