godirwalk เป็นห้องสมุดสำหรับการสำรวจแผนผังไดเรกทอรีบนระบบไฟล์
ในระยะสั้นทำไมฉันถึงสร้างห้องสมุดนี้?
filepath.Walkfilepath.Walkfilepath.Walkfilepath.Walkขึ้นอยู่กับสถานการณ์เฉพาะของคุณคุณอาจไม่จำเป็นต้องมีห้องสมุดสำหรับการเดินในไฟล์อีกต่อไป
ตัวอย่างเพิ่มเติมมีอยู่ใน examples/ ไดเรกทอรีย่อย
ไลบรารีนี้จะทำให้ชื่อไดเรกทอรีระดับบนสุดที่ให้ไว้ตามตัวคั่นพา ธ เฉพาะของระบบปฏิบัติการโดยเรียก filepath.Clean ในอาร์กิวเมนต์แรก อย่างไรก็ตามมันจะให้ชื่อพา ธ ที่สร้างขึ้นโดยใช้ตัวคั่นพาพา ธ เฉพาะระบบปฏิบัติการที่ถูกต้องเมื่อเรียกใช้ฟังก์ชันการเรียกกลับที่ให้ไว้
dirname := "some/directory/root"
err := godirwalk . Walk ( dirname , & godirwalk. Options {
Callback : func ( osPathname string , de * godirwalk. Dirent ) error {
// Following string operation is not most performant way
// of doing this, but common enough to warrant a simple
// example here:
if strings . Contains ( osPathname , ".git" ) {
return godirwalk . SkipThis
}
fmt . Printf ( "%s %s n " , de . ModeType (), osPathname )
return nil
},
Unsorted : true , // (optional) set true for faster yet non-deterministic enumeration (see godoc)
}) ไลบรารีนี้ไม่เพียง แต่ให้ฟังก์ชั่นสำหรับการสำรวจแผนผังไดเรกทอรีระบบไฟล์ แต่ยังสำหรับการได้รับรายการลูกหลานทันทีของไดเรกทอรีเฉพาะซึ่งโดยทั่วไปจะเร็วกว่าการใช้ os.ReadDir หรือ os.ReadDirnames
นี่คือเหตุผลที่ฉันใช้ godirwalk ในการตั้งค่า to filepath.Walk , os.ReadDir และ os.ReadDirnames
filepath.Walk เมื่อเปรียบเทียบกับ filepath.Walk ในเกณฑ์มาตรฐานจะพบว่ามีความเร็วระหว่างห้าถึงสิบเท่าของดาร์วินที่ความเร็วเทียบเท่ากับของยูทิลิตี้ UNIX find และประมาณสองเท่าของความเร็วใน Linux; และประมาณสี่เท่าของความเร็วบนหน้าต่าง
มันจะได้รับประสิทธิภาพที่เพิ่มขึ้นนี้ได้อย่างไร? มันทำงานได้น้อยลงเพื่อให้คุณได้รับผลผลิตเกือบเท่ากัน ไลบรารีนี้เรียกฟังก์ชั่น syscall เดียวกันเพื่อทำงาน แต่มันทำให้การโทรน้อยลงไม่ทิ้งข้อมูลที่อาจต้องการและสร้างหน่วยความจำน้อยลงระหว่างทางด้วยการนำบัฟเฟอร์รอยขีดข่วนเดียวกันกลับมาอ่านจากไดเรกทอรีแทนที่จะจัดสรรบัฟเฟอร์ใหม่ทุกครั้งที่อ่านข้อมูลการป้อนข้อมูลระบบไฟล์
ในขณะที่ข้ามแผนผังไดเรกทอรีระบบไฟล์ filepath.Walk ได้รับรายการลูกหลานทันทีของไดเรกทอรีและทิ้งข้อมูลประเภทโหนดสำหรับรายการระบบไฟล์ที่จัดทำโดยระบบปฏิบัติการที่มาพร้อมกับชื่อของโหนด จากนั้นทันทีก่อนที่จะเรียกใช้ฟังก์ชันการโทรกลับ filepath.Walk เรียกใช้ os.Stat สำหรับแต่ละโหนดและส่งผ่านข้อมูล os.FileInfo ที่ส่งคืนไปยังการโทรกลับ
ในขณะที่ข้อมูล os.FileInfo ที่จัดทำโดย os.Stat มีประโยชน์อย่างยิ่ง-และรวมถึงข้อมูล os.FileMode หากต้องใช้การเรียกระบบเพิ่มเติมสำหรับแต่ละโหนด
เนื่องจากการโทรกลับส่วนใหญ่สนใจเพียงแค่ประเภทโหนดคือไลบรารีนี้ไม่ได้ทิ้งข้อมูลประเภทออกไป แต่ให้ข้อมูลนั้นไปยังฟังก์ชั่นการโทรกลับในรูปแบบของค่า os.FileMode โปรดทราบว่าค่า os.FileMode ที่ให้ไว้ซึ่งไลบรารีนี้ให้ข้อมูลประเภทโหนดเท่านั้นและไม่มีบิตอนุญาตบิตเหนียวหรือข้อมูลอื่น ๆ จากโหมดไฟล์ หากการโทรกลับมีความสนใจเกี่ยวกับโครงสร้างข้อมูล os.FileInfo ทั้งหมดของโหนดโดยเฉพาะการโทรกลับสามารถเรียกใช้ os.Stat ได้อย่างง่ายดายเมื่อจำเป็นและเฉพาะเมื่อจำเป็น
$ go test -bench=. -benchmem
goos: darwin
goarch: amd64
pkg: github.com/karrick/godirwalk
BenchmarkReadDirnamesStandardLibrary-12 50000 26250 ns/op 10360 B/op 16 allocs/op
BenchmarkReadDirnamesThisLibrary-12 50000 24372 ns/op 5064 B/op 20 allocs/op
BenchmarkFilepathWalk-12 1 1099524875 ns/op 228415912 B/op 416952 allocs/op
BenchmarkGodirwalk-12 2 526754589 ns/op 103110464 B/op 451442 allocs/op
BenchmarkGodirwalkUnsorted-12 3 509219296 ns/op 100751400 B/op 378800 allocs/op
BenchmarkFlameGraphFilepathWalk-12 1 7478618820 ns/op 2284138176 B/op 4169453 allocs/op
BenchmarkFlameGraphGodirwalk-12 1 4977264058 ns/op 1031105328 B/op 4514423 allocs/op
PASS
ok github.com/karrick/godirwalk 21.219s$ go test -bench=. -benchmem
goos: linux
goarch: amd64
pkg: github.com/karrick/godirwalk
BenchmarkReadDirnamesStandardLibrary-12 100000 15458 ns/op 10360 B/op 16 allocs/op
BenchmarkReadDirnamesThisLibrary-12 100000 14646 ns/op 5064 B/op 20 allocs/op
BenchmarkFilepathWalk-12 2 631034745 ns/op 228210216 B/op 416939 allocs/op
BenchmarkGodirwalk-12 3 358714883 ns/op 102988664 B/op 451437 allocs/op
BenchmarkGodirwalkUnsorted-12 3 355363915 ns/op 100629234 B/op 378796 allocs/op
BenchmarkFlameGraphFilepathWalk-12 1 6086913991 ns/op 2282104720 B/op 4169417 allocs/op
BenchmarkFlameGraphGodirwalk-12 1 3456398824 ns/op 1029886400 B/op 4514373 allocs/op
PASS
ok github.com/karrick/godirwalk 19.179sfilepath.Walkก่อนหน้านี้ฉันไม่เคยสนใจเรื่องนี้ แต่มีอารมณ์ขันฉัน เราทุกคนรักวิธีที่เราสามารถเขียนได้ครั้งเดียวและวิ่งไปทุกที่ มันเป็นสิ่งสำคัญสำหรับการยอมรับการเติบโตและความสำเร็จของภาษาที่ซอฟต์แวร์ที่เราสร้างขึ้นสามารถทำงานได้โดยไม่ได้รับการแก้ไขในสถาปัตยกรรมและระบบปฏิบัติการทั้งหมดที่สนับสนุนโดย GO
เมื่อระบบไฟล์แบบ traversed มีลูปตรรกะที่เกิดจากการเชื่อมโยงสัญลักษณ์ไปยังไดเรกทอรีบน Unix filepath.Walk จะละเว้นลิงก์สัญลักษณ์และข้ามต้นไม้ไดเรกทอรีทั้งหมดโดยไม่มีข้อผิดพลาด อย่างไรก็ตามใน Windows, filepath.Walk จะดำเนินการตามลิงก์สัญลักษณ์ไดเรกทอรีต่อไปแม้ว่ามันจะไม่ควรจะเป็นเพราะในที่สุดก็ทำให้ filepath.Walk ที่จะยุติก่อนและส่งคืนข้อผิดพลาดเมื่อชื่อพา ธ ได้นานเกินไปจากการเชื่อมโยงที่ไม่มีที่สิ้นสุดของลิงก์สัญลักษณ์ ข้อผิดพลาดนี้มาจาก Windows ผ่าน filepath.Walk และไปยังไคลเอนต์ต้นน้ำที่ใช้งาน filepath.Walk
Takeaway คือพฤติกรรมนั้นแตกต่างกันไปตามแพลตฟอร์มใดที่ filepath.Walk กำลังทำงานอยู่ แม้ว่าสิ่งนี้จะไม่ได้ตั้งใจอย่างชัดเจนจนกว่าจะได้รับการแก้ไขในไลบรารีมาตรฐาน แต่ก็มีปัญหาความเข้ากันได้
ไลบรารีนี้แก้ไขปัญหาข้างต้นซึ่งจะไม่ติดตามไฟล์ Sytem Logical บน Unix หรือ Windows นอกจากนี้มันจะตามลิงค์สัญลักษณ์เมื่อมีการตั้งค่า FollowSymbolicLinks ตาม Symbolic Link พฤติกรรมบน Windows และระบบปฏิบัติการอื่น ๆ นั้นเหมือนกัน
filepath.Walk ในขณะที่ห้องสมุดนี้มุ่งมั่นที่จะเลียนแบบพฤติกรรมของ filepath.Walk มาตรฐานที่เขียนขึ้นมาอย่างไม่น่าเชื่อ แต่มีสถานที่ที่เบี่ยงเบนไปเล็กน้อยเพื่อให้อินเทอร์เฟซผู้โทรง่ายหรือใช้งานง่ายขึ้น
เนื่องจากไลบรารีนี้ไม่ได้เรียกใช้ os.Stat บนทุกระบบไฟล์โหนดที่พบจึงไม่มีเหตุการณ์ข้อผิดพลาดที่เป็นไปได้สำหรับฟังก์ชั่นการโทรกลับเพื่อกรอง อาร์กิวเมนต์ที่สามในลายเซ็นฟังก์ชั่น filepath.WalkFunc เพื่อผ่านข้อผิดพลาดจาก os.Stat ไปยังฟังก์ชั่นการโทรกลับไม่จำเป็นอีกต่อไปและกำจัดออกจากลายเซ็นของฟังก์ชั่นการเรียกกลับจากไลบรารีนี้
นอกจากนี้ความแตกต่างของอินเทอร์เฟซเล็กน้อยนี้ระหว่าง filepath.WalkFunc และ WalkFunc ของไลบรารีนี้จะช่วยลดรหัสหม้อไอน้ำที่ตัวจัดการการโทรกลับจะต้องเขียนเมื่อพวกเขาใช้ filepath.Walk แทนที่จะใช้ฟังก์ชั่นการโทรกลับทุกครั้งที่จำเป็นต้องตรวจสอบค่าความผิดพลาดที่ส่งผ่านไปและสาขาตามนั้นผู้ใช้ไลบรารีนี้ไม่มีค่าข้อผิดพลาดในการตรวจสอบทันทีเมื่อเข้าสู่ฟังก์ชั่นการโทรกลับ นี่คือการปรับปรุงทั้งในประสิทธิภาพรันไทม์และความชัดเจนของรหัส
ในทุกแพลตฟอร์มระบบปฏิบัติการ filepath.Walk เรียกใช้ฟังก์ชั่นการโทรกลับด้วยชื่อพา ธ ที่คั่นด้วยโซลูส ( / ) ในทางตรงกันข้ามไลบรารีนี้เรียกใช้การโทรกลับด้วยตัวคั่นชื่อพา ธ เฉพาะระบบปฏิบัติการโดยไม่คำนึงถึงการเรียกไปยัง filepath.Clean ในฟังก์ชั่นการเรียกกลับสำหรับแต่ละโหนดก่อนที่จะใช้ชื่อพา ธ ที่ให้ไว้
กล่าวอีกนัยหนึ่งแม้ใน Windows, filepath.Walk จะเรียกใช้การโทรกลับด้วย some/path/to/foo.txt ซึ่งต้องการลูกค้าที่เขียนเป็นอย่างดีเพื่อทำการตั้งชื่อเส้นทางการใช้งานสำหรับทุกไฟล์ก่อนที่จะทำงานกับไฟล์ที่ระบุ นี่คือข้อกำหนดของแผ่นหม้อต้มที่ซ่อนอยู่ในการสร้างฟังก์ชั่นการโทรกลับผู้ไม่เชื่อเรื่องพระเจ้าอย่างแท้จริง ในความเป็นจริงลูกค้าจำนวนมากพัฒนาบน Unix และไม่ได้ทดสอบใน Windows ที่ละเลยความละเอียดอ่อนนี้และจะส่งผลให้เกิดข้อบกพร่องของซอฟต์แวร์เมื่อมีคนพยายามเรียกใช้ซอฟต์แวร์นั้นบน Windows
ไลบรารีนี้เรียกใช้ฟังก์ชั่นการโทรกลับด้วย somepathtofoo.txt สำหรับไฟล์เดียวกันเมื่อทำงานบน Windows ไม่จำเป็นต้องทำให้ชื่อพา ธ ของลูกค้าเป็นปกติและลดโอกาสที่ไคลเอนต์จะทำงานบน UNIX
การปรับปรุงนี้ช่วยลดความจำเป็นสำหรับรหัสหม้อไอน้ำในฟังก์ชั่นการโทรกลับในขณะที่ปรับปรุงประสิทธิภาพการทำงานของไลบรารีนี้
godirwalk.SkipThis ใช้งานง่ายกว่าใช้มากกว่า filepath.SkipDir แง่มุมที่สับสนอย่างหนึ่งของอินเทอร์เฟซ filepath.WalkFunc ที่ไลบรารีนี้ต้องเลียนแบบคือวิธีที่ผู้โทรบอกฟังก์ชัน Walk เพื่อข้ามรายการระบบไฟล์ ด้วยทั้ง filepath.Walk และ Walk ของห้องสมุดนี้เมื่อฟังก์ชั่นการโทรกลับต้องการข้าม filepath.SkipDir และไม่ลงมาสู่ลูก ๆ ของมัน หากฟังก์ชั่นการโทรกลับส่งคืน filepath.SkipDir สำหรับ non-directory, filepath.Walk และไลบรารีนี้จะหยุดประมวลผลรายการเพิ่มเติมในไดเรกทอรีปัจจุบัน นี่ไม่ใช่สิ่งที่นักพัฒนาส่วนใหญ่ต้องการหรือคาดหวัง หากคุณต้องการข้ามรายการที่ไม่ใช่ไดเรกทอรีโดยเฉพาะ แต่ดำเนินการประมวลผลรายการในไดเรกทอรีต่อไปฟังก์ชั่นการโทรกลับจะต้องส่งคืน NIL
ความหมายของการออกแบบอินเทอร์เฟซนี้คือเมื่อคุณต้องการเดินตามลำดับชั้นของระบบไฟล์และข้ามรายการคุณต้องส่งคืนค่าที่แตกต่างกันตามประเภทของรายการระบบไฟล์ที่โหนดคือ ในการข้ามรายการหากรายการเป็นไดเรกทอรีคุณต้องส่งคืน filepath.SkipDir และหากรายการไม่ใช่ไดเรกทอรีคุณต้องส่งคืน nil นี่เป็นอุปสรรค์ที่โชคร้ายที่ฉันสังเกตเห็นนักพัฒนาหลายคนที่ต้องดิ้นรนเพียงเพราะมันไม่ใช่อินเทอร์เฟซที่ใช้งานง่าย
นี่คือฟังก์ชั่นการโทรกลับตัวอย่างที่ยึดติดกับอินเทอร์เฟซ filepath.WalkFunc เพื่อให้ข้ามรายการระบบไฟล์ใด ๆ ที่มีชื่อพา ธ แบบเต็มรวมถึงสตริงย่อยเฉพาะ optSkip โปรดทราบว่าไลบรารีนี้ยังคงรองรับพฤติกรรมที่เหมือนกันของ filepath.Walk เมื่อฟังก์ชั่นการโทรกลับส่งคืน filepath.SkipDir
func callback1 ( osPathname string , de * godirwalk. Dirent ) error {
if optSkip != "" && strings . Contains ( osPathname , optSkip ) {
if b , err := de . IsDirOrSymlinkToDir (); b == true && err == nil {
return filepath . SkipDir
}
return nil
}
// Process file like normal...
return nil
} ไลบรารีนี้พยายามกำจัดตรรกะบางส่วนที่จำเป็นในฟังก์ชั่นการโทรกลับโดยให้ค่าข้อผิดพลาดโทเค็นใหม่ SkipThis ซึ่งฟังก์ชั่นการโทรกลับอาจกลับไปข้ามรายการระบบไฟล์ปัจจุบันโดยไม่คำนึงถึงประเภทของรายการ หากรายการปัจจุบันเป็นไดเรกทอรีลูก ๆ ของมันจะไม่ได้รับการแจกแจงราวกับว่าการโทรกลับได้ส่งคืน filepath.SkipDir หากรายการปัจจุบันไม่ใช่ไดเรกทอรีรายการระบบไฟล์ถัดไปในไดเรกทอรีปัจจุบันจะถูกแจกแจงตรงข้ามราวกับว่าการโทรกลับ nil ฟังก์ชั่นการเรียกกลับตัวอย่างต่อไปนี้มีพฤติกรรมที่เหมือนกันเหมือนก่อนหน้านี้ แต่มีแผ่นอุลเลอร์น้อยกว่าและตรรกะที่เป็นที่ยอมรับซึ่งฉันพบง่ายกว่าที่จะติดตาม
func callback2 ( osPathname string , de * godirwalk. Dirent ) error {
if optSkip != "" && strings . Contains ( osPathname , optSkip ) {
return godirwalk . SkipThis
}
// Process file like normal...
return nil
}filepath.Walk พฤติกรรมเริ่มต้นของไลบรารีนี้คือการเพิกเฉยต่อการเชื่อมโยงสัญลักษณ์ไปยังไดเรกทอรีเมื่อเดินต้นไม้ไดเรกทอรีเช่นเดียวกับ filepath.Walk อย่างไรก็ตามมันเรียกใช้ฟังก์ชั่นการเรียกกลับด้วยแต่ละโหนดที่พบรวมถึงลิงก์สัญลักษณ์ หากมีกรณีการใช้งานโดยเฉพาะเพื่อติดตามลิงก์สัญลักษณ์เมื่อข้ามทรีไดเรกทอรีไลบรารีนี้สามารถเรียกใช้ในลักษณะที่จะทำเช่นนั้นโดยการตั้งค่าพารามิเตอร์การกำหนด FollowSymbolicLinks ตาม true
พฤติกรรมเริ่มต้นของไลบรารีนี้คือการจัดเรียงลูกหลานทันทีของไดเรกทอรีก่อนที่จะไปที่แต่ละโหนดเช่นเดียวกับ filepath.Walk ทำ นี่เป็นพฤติกรรมที่ต้องการ อย่างไรก็ตามสิ่งนี้มีประสิทธิภาพเล็กน้อยและการลงโทษหน่วยความจำที่จำเป็นในการเรียงลำดับชื่อเมื่อโหนดไดเรกทอรีมีหลายรายการ นอกจากนี้หากผู้โทรระบุการแจงนับ Unsorted ในพารามิเตอร์การกำหนดค่าไดเรกทอรีการอ่านจะดำเนินการอย่างเกียจคร้านเมื่อผู้โทรใช้รายการ หากมีกรณีการใช้งานเฉพาะที่ไม่จำเป็นต้องมีการเรียงลำดับลูกหลานทันทีของไดเรกทอรีก่อนที่จะไปที่โหนดของมันไลบรารีนี้จะข้ามขั้นตอนการเรียงลำดับเมื่อพารามิเตอร์ Unsorted ตั้งค่าเป็น true
นี่คือการอ่านที่น่าสนใจเกี่ยวกับการเคลื่อนที่ที่อาจเกิดขึ้นจากการสำรวจลำดับชั้นของระบบไฟล์ตามลำดับที่ไม่ได้กำหนด หากคุณรู้ว่าปัญหาที่คุณกำลังแก้ไขจะไม่ได้รับผลกระทบจากไฟล์คำสั่งซื้อฉันขอแนะนำให้คุณใช้ Unsorted มิฉะนั้นข้ามการตั้งค่าตัวเลือกนี้
นักวิจัยพบข้อผิดพลาดในสคริปต์ Python อาจส่งผลกระทบต่อการศึกษาหลายร้อยครั้ง
ไลบรารีนี้ให้รหัสต้นน้ำที่มีความสามารถในการระบุฟังก์ชั่นการโทรกลับที่จะเรียกใช้สำหรับแต่ละไดเรกทอรีหลังจากที่เด็กถูกประมวลผล สิ่งนี้ถูกใช้เพื่อลบไดเรกทอรีที่ว่างเปล่าซ้ำหลังจากข้ามระบบไฟล์อย่างมีประสิทธิภาพมากขึ้น ดู examples/clean-empties สำหรับตัวอย่างของการใช้งานนี้
ไลบรารีนี้ให้รหัสต้นน้ำที่มีความสามารถในการระบุการโทรกลับที่จะเรียกใช้สำหรับข้อผิดพลาดที่ระบบปฏิบัติการส่งคืนช่วยให้รหัสต้นน้ำสามารถกำหนดแนวทางการดำเนินการต่อไปที่จะหยุดการเดินตามลำดับชั้นเพราะมันจะไม่มีข้อผิดพลาดโทรกลับหรือข้ามโหนดที่เกิดข้อผิดพลาด ดู examples/walk-fast สำหรับตัวอย่างของการใช้งานนี้