อะไรขยายไปยังไฟล์ทั้งหมดในไดเร็กทอรีปัจจุบันแบบวนซ้ำ


92

ฉันรู้ว่า**/*.extขยายไปยังไฟล์ทั้งหมดในไดเร็กทอรีย่อยทั้งหมดที่ตรงกัน*.extแต่ส่วนขยายที่คล้ายกันที่รวมไฟล์ดังกล่าวทั้งหมดในไดเร็กทอรีปัจจุบันด้วยคืออะไร


4
**/*.extทุบตีฉันไม่ได้จัดการ คุณแน่ใจหรือไม่ว่าจะได้ผลสำหรับคุณ
Tangens

@tangens คุณต้องเปิดใช้งานglobstarตัวเลือกตามคำตอบของเดนนิส
kenorb

คำตอบ:


111

สิ่งนี้จะใช้ได้ใน Bash 4:

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

เพื่อให้ลูกโลกเครื่องหมายดอกจันคู่ทำงานได้globstarจำเป็นต้องตั้งค่าตัวเลือก (ค่าเริ่มต้น: เปิด):

shopt -s globstar

จากman bash:

    globstar
                  หากตั้งค่ารูปแบบ ** จะใช้ในการขยายชื่อไฟล์ตาม
                  ข้อความจะตรงกับไฟล์และไดเร็กทอรีศูนย์หรือมากกว่าและ
                  ไดเรกทอรีย่อย ถ้ารูปแบบตามด้วย / ให้เท่านั้น
                  ไดเรกทอรีและไดเรกทอรีย่อยตรงกัน

ตอนนี้ฉันกำลังสงสัยว่าครั้งหนึ่งอาจมีข้อผิดพลาดในการประมวลผลของ globstar หรือไม่เพราะตอนนี้ใช้เพียงแค่ls **/*.extฉันได้ผลลัพธ์ที่ถูกต้อง

ไม่ว่าฉันจะดูที่การวิเคราะห์ kenorb โดยใช้ที่เก็บ VLC และพบปัญหาบางอย่างกับการวิเคราะห์นั้นและในคำตอบของฉันด้านบนทันที:

การเปรียบเทียบกับเอาต์พุตของfindคำสั่งนั้นไม่ถูกต้องเนื่องจากการระบุ-type fไม่รวมไฟล์ประเภทอื่น ๆ (โดยเฉพาะไดเร็กทอรี) และlsคำสั่งที่อยู่ในรายการอาจเป็นไปได้ นอกจากนี้หนึ่งในคำสั่งที่อยู่ในรายการls -1 {,**/}*.*ซึ่งดูเหมือนจะขึ้นอยู่กับของฉันด้านบนจะแสดงเฉพาะชื่อที่มีจุดสำหรับไฟล์ที่อยู่ในไดเรกทอรีย่อย คำถามของ OP และคำตอบของฉันมีจุดเนื่องจากสิ่งที่กำลังค้นหาคือไฟล์ที่มีนามสกุลเฉพาะ

ที่สำคัญที่สุดก็คือว่ามีปัญหาพิเศษโดยใช้คำสั่งที่มีรูปแบบls GLOBSTAR **รายการที่ซ้ำกันจำนวนมากเกิดขึ้นเนื่องจากรูปแบบถูกขยายโดย Bash ไปยังชื่อไฟล์ทั้งหมด (และชื่อไดเร็กทอรี) ในโครงสร้างที่กำลังตรวจสอบ หลังจากการขยายlsคำสั่งจะแสดงรายการแต่ละรายการและเนื้อหาหากเป็นไดเร็กทอรี

ตัวอย่าง:

ในไดเร็กทอรีปัจจุบันของเราคือไดเร็กทอรีย่อยAและเนื้อหา:

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

ในแผนภูมิ**ขยายเป็น "AA / AB A / AB / ABC A / AB / ABC / ABC1 A / AB / ABC / ABC2 A / AB / ABC / ABCD A / AB / ABC / ABCD / ABCD1" (7 รายการ) . หากคุณทำecho **นั่นคือผลลัพธ์ที่แน่นอนที่คุณจะได้รับและแต่ละรายการจะแสดงครั้งเดียว อย่างไรก็ตามหากคุณทำls **มันจะแสดงรายการของแต่ละรายการเหล่านั้น โดยพื้นฐานแล้วจะls Aตามด้วยls A/ABฯลฯ ดังนั้นจึงA/ABแสดงสองครั้ง นอกจากนี้lsกำลังจะแยกเอาต์พุตของไดเรกทอรีย่อยแต่ละรายการออกจากกัน:

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

ดังนั้นการใช้การwc -lนับบรรทัดว่างเหล่านั้นและส่วนหัวของชื่อไดเร็กทอรีทั้งหมดซึ่งจะทำให้การนับห่างออกไป

นี้เลยเหตุผลที่คุณไม่ควรอีกแยกls

จากการวิเคราะห์เพิ่มเติมนี้ฉันไม่แนะนำให้ใช้รูปแบบ globstar ในกรณีใด ๆ นอกเหนือจากการวนซ้ำบนต้นไม้ของไฟล์ในลักษณะนี้:

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

ในการเปรียบเทียบขั้นสุดท้ายฉันใช้ที่เก็บซอร์ส Bash ที่ฉันมีสะดวกและทำสิ่งนี้:

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

ฉันเคยtrเปลี่ยนช่องว่างเป็นขึ้นบรรทัดใหม่ซึ่งใช้ได้เฉพาะที่นี่เนื่องจากไม่มีการเว้นวรรคชื่อ ฉันเคยsedลบชั้นนำ./ออกจากแต่ละบรรทัดของเอาต์พุตจากfind. ฉันเรียงลำดับผลลัพธ์findเนื่องจากโดยปกติจะไม่มีการเรียงลำดับและการขยายตัวของ Bash นั้นถูกจัดเรียงเรียบร้อยแล้ว ในขณะที่คุณสามารถดูการส่งออกเพียงอย่างเดียวจากdiffเป็นไดเรกทอรีปัจจุบันเอาท์พุท. findเมื่อฉันทำls ** | wc -lเอาต์พุตมีจำนวนบรรทัดเกือบสองเท่า


5
ฉันทดสอบ Ubuntu และ Cygwin และglobstarเป็นค่าเริ่มต้นoff
Steven Penny

12
คำตอบที่ดีที่สุด! แต่ฉันคิดว่า**/*.extน่าจะเพียงพอแล้ว shopt -s dotglobนอกจากนี้คุณจะไม่ได้มีไฟล์ที่ซ่อนอยู่เว้นแต่คุณ
gniourf_gniourf

2
ปิดการใช้งานglobstar: shopt -u globstar.
kenorb

4
@gniourf_gniourf คำถามจริงขอให้รวมไดเรกทอรีปัจจุบันโดยเฉพาะดังนั้นไม่**/*.extไม่เพียงพอ
msciwoj

2
@dotnetCarpenter: เวอร์ชันของ Bash ที่มาพร้อมกับ MacOS คือ 3.2 ซึ่งไม่รองรับ globstar อย่างที่คุณพบ เครื่องหมายดอกจันคู่จะถือว่าเหมือนกับดอกจันเดียว Globstar เปิดตัวใน Bash 4.0
หยุดชั่วคราวจนกว่าจะมีประกาศอีกครั้ง

13

สิ่งนี้จะพิมพ์ไฟล์ทั้งหมดในไดเร็กทอรีปัจจุบันและไดเร็กทอรีย่อยซึ่งลงท้ายด้วย ".ext"

find . -name '*.ext' -print

แม้ว่าคำตอบนี้จะไม่ตรงตาม "การขยาย" ที่ขอโดย OP ในแง่ที่เข้มงวดที่สุด แต่ก็มักจะให้ผลลัพธ์ที่ต้องการ
หยุดชั่วคราวจนกว่าจะมีประกาศอีกครั้ง

7

คุณสามารถใช้: **/*.*จะรวมไฟล์ทั้งหมดซ้ำ (เปิดใช้งานโดย: shopt -s globstar)

โปรดดูการทดสอบรูปแบบอื่น ๆ ด้านล่างและวิธีการทำงาน


การทดสอบโฟลเดอร์ที่มีไฟล์ 3472 ในโฟลเดอร์ที่เก็บVLCตัวอย่าง:

(ไฟล์ของ 3472 นับเป็นต่อทั้งหมด: find . -type f | wc -l)

  • ls -1 **/*.* - ส่งคืน 3338
  • ls -1 {,**/}*.*- ส่งคืน 3341 (ตามที่เสนอโดยDennis )
  • ls -1 {,**/}* - ส่งคืน 8265
  • ls -1 **/*- ส่งคืน 7817 ยกเว้นไฟล์ที่ซ่อนอยู่ (ตามที่เสนอโดยDennis )
  • ls -1 **/{.[^.],}*- ส่งคืน 7869 (ตามที่เดนนิสเสนอ)
  • ls -1 {,**/}.?* - ส่งคืน 15855
  • ls -1 {,**/}.* - ส่งคืน 20321

ดังนั้นฉันคิดว่าวิธีที่ใกล้เคียงที่สุดในการแสดงรายการไฟล์ทั้งหมดแบบวนซ้ำคือตัวอย่างแรก ( **/*.*) ตามความคิดเห็นของ gniourf-gniourf (สมมติว่าไฟล์มีนามสกุลที่เหมาะสมหรือใช้ไฟล์ที่เฉพาะเจาะจง) เนื่องจากตัวอย่างที่สองมีรายการที่ซ้ำกันน้อยกว่าดังต่อไปนี้ :

$ 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

และอีกรายสร้างรายการซ้ำเพิ่มเติม


ในการรวมไฟล์ที่ซ่อนไว้ให้ใช้: shopt -s dotglob(ปิดการใช้งานโดยshopt -u dotglob) ไม่แนะนำเนื่องจากอาจส่งผลต่อคำสั่งเช่นmvหรือrmและคุณสามารถลบไฟล์ที่ไม่ถูกต้องโดยไม่ตั้งใจ


บนเทอร์มินัล Mac และทุบตีที่เปิดใช้งาน globstar ฉันพบวิธีแก้ปัญหาข้างต้น ( **/*.*) ให้ข้อมูลและทำงานได้ดีที่สุด คำตอบที่ยอมรับทำให้เกิดรายการที่ซ้ำกันในไดเรกทอรีด้านบน รูปแบบการทำงานของฉันคือ:"${path}"**/*.*
mummybot

มันน่าสนใจที่จะลองใช้ตัวเลือกอื่น ๆ เช่น nullglob และ dotglob
Wilf

4
$ find . -type f

ซึ่งจะแสดงรายการไฟล์ทั้งหมดในไดเร็กทอรีปัจจุบัน จากนั้นคุณสามารถทำคำสั่งอื่น ๆ กับเอาต์พุตโดยใช้ -exec

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

ซึ่งจะ grep แต่ละไฟล์จากการค้นหาสำหรับสตริง "foo"


ตอนนี้เป็นเวลา 11 ปีต่อมาอาจถึงเวลาที่มีคนชี้ให้เห็นว่าfind . -type fใช้ซ้ำกับ root ที่ไดเร็กทอรีปัจจุบันไม่ใช่เฉพาะกับไดเร็กทอรีปัจจุบัน
Roger Dahl

4

ทำไมไม่ใช้เพียงแค่ขยายวงเล็บปีกกาเพื่อรวมไดเรกทอรีปัจจุบันด้วย

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

การขยายตัวของ Brace เกิดขึ้นก่อนการขยายแบบ glob ดังนั้นคุณสามารถทำสิ่งที่คุณต้องการได้อย่างมีประสิทธิภาพด้วย bash เวอร์ชันเก่าและสามารถละทิ้งการใช้ globstar ในเวอร์ชันใหม่

นอกจากนี้ยังถือเป็นแนวทางปฏิบัติที่ดีในการทุบตีเพื่อรวมความเป็นผู้นำ./ในรูปแบบลูกโลกของคุณ

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