เหตุใดบางครั้งการค้นหาจึงจับคู่อาร์กิวเมนต์ของพา ธ บรรทัดคำสั่ง


9

บน Linux

cd /tmp
mkdir foo; cd foo

ตอนนี้ทำงาน

find . -name 'foo'

ไม่ให้ผลลัพธ์ ในขณะที่วิ่ง

find /tmp/foo -name 'foo'

ให้ผลลัพธ์/tmp/fooที่ไม่สมเหตุสมผลกับฉัน มีใครอธิบายได้ไหม


2
ค้นหา seaches ภายในเส้นทางที่กำหนดรวมอยู่ตามที่ระบุไว้ ดังนั้นหากคุณป้อนข้อมูล./จะไม่ตรงกันfoo
Costas

@Costas: ฉันเข้าใจว่า findสิ่งที่ผมไม่เข้าใจคือทำไมมันได้สร้างความแตกต่างถ้าฉันให้เส้นทางสัมบูรณ์ไปยัง
York

@York ในบางสถานการณ์ไม่มีพฤติกรรมที่ถูกต้องเสมอไป ลองนึกภาพ symlink ด้วยชื่อbarที่ชี้ไปยังไฟล์fooที่อยู่นอกเส้นทางการค้นหา ตรงกับที่ว่าหรือไม่?
Hauke ​​Laging

1
.และ/tmp/fooจะไม่เหมือนกัน - พวกเขาเป็นสองเชื่อมโยงอย่างหนักที่แตกต่างกันไปยังไดเรกทอรีเดียวกัน; find /tmp/foo/. -name 'foo'ไม่พบอะไรเลย
jimmij

@jimmij: เมื่อฉันเรียกใช้find /tmp/foo -name 'foo'ฉันขอให้ทุบตีหาในไดเรกทอรี/tmp/fooไฟล์ที่มีชื่อว่า "foo" เนื่องจากไดเรกทอรี/tmp/fooว่างเปล่าจึงไม่ควรส่งคืนสิ่งใด ฉันไม่เข้าใจว่าทำไมมันกลับ/tmp/fooมา ในทางกลับกันเมื่อฉันวิ่งfind . -name 'foo'ฉันก็ขอทุบตีสิ่งเดียวกันนั่นคือการค้นหาไฟล์ในไดเรกทอรีปัจจุบัน (ซึ่งเกิดขึ้นได้/tmp/foo) ซึ่งมีชื่อว่า 'foo' และจะไม่ส่งคืนสิ่งใดที่สมเหตุสมผล
York

คำตอบ:


15

findสำรวจแผนผังไดเรกทอรีที่ระบุและประเมินค่านิพจน์ที่กำหนดสำหรับแต่ละไฟล์ที่พบ การสำรวจเส้นทางเริ่มต้นที่เส้นทางที่กำหนด นี่คือบทสรุปของการfind . -name fooทำงาน:

  • เส้นทางแรกในบรรทัดคำสั่ง: .
    • ชื่อฐาน ( .) ตรงกับรูปแบบfooหรือไม่ ไม่ทำอะไรเลย
      มันเกิดขึ้นนั่น/tmp/fooคือชื่ออื่นสำหรับไดเรกทอรีเดียวกัน แต่findไม่รู้ว่า (และไม่ควรลองค้นหา)
    • เส้นทางเป็นไดเรกทอรีหรือไม่? ใช่เลย ระบุรายการใน.และสำหรับแต่ละรายการให้ดำเนินการผ่านเส้นทาง
      • ไดเรกทอรีว่างเปล่า: มันไม่มีรายการอื่นนอกจาก.และ..ซึ่งfindไม่ได้วนซ้ำ ดังนั้นงานเสร็จแล้ว

และfind /tmp/foo:

  • เส้นทางแรกในบรรทัดคำสั่ง: /tmp/foo
    • ชื่อฐาน ( foo) ตรงกับรูปแบบfooหรือไม่ ใช่แล้วเงื่อนไขนั้นตรงกัน
      • ไม่มีการดำเนินการที่เกี่ยวข้องกับเงื่อนไขนี้ดังนั้นให้ดำเนินการตามค่าเริ่มต้นซึ่งก็คือพิมพ์เส้นทาง
    • เส้นทางเป็นไดเรกทอรีหรือไม่? ใช่เลย ระบุรายการใน/tmp/fooและสำหรับแต่ละรายการให้ดำเนินการผ่านเส้นทาง
      • ไดเรกทอรีว่างเปล่า: มันไม่มีรายการอื่นนอกจาก.และ..ซึ่งfindไม่ได้วนซ้ำ ดังนั้นงานเสร็จแล้ว

มันเกิดขึ้นอย่างนั้น.และ/tmp/fooเป็นไดเรกทอรีเดียวกัน แต่ไม่เพียงพอที่จะรับประกันว่าfindมีพฤติกรรมเดียวกันทั้งสองอย่าง findคำสั่งมีวิธีการที่จะแยกแยะความแตกต่างระหว่างเส้นทางไปยังไฟล์เดียวกัน; ภาค-nameแสดงเป็นหนึ่งในนั้น find /tmp/foo -name fooตรงกับไดเรกทอรีเริ่มต้นเช่นเดียวกับไฟล์ใด ๆ fooภายใต้มันที่เรียกว่า find . -name .ตรงกับไดเรกทอรีเริ่มต้นเท่านั้น ( .ไม่สามารถพบได้ในระหว่างการสำรวจเส้นทางแบบเรียกซ้ำ)


"มันไม่มีรายการอื่นนอกจาก. และ .. ซึ่งการค้นหานั้นไม่ได้วนซ้ำ" ฉันถือว่า "ไม่ได้วนซ้ำ" อ้างอิงถึง ".. " ถ้านั่นถูกต้องแล้ว "transverse recursively" หมายถึงอะไรในบริบทของ ".. "?
Faheem Mitha

@FaheemMitha“ไม่ขวางซ้ำ” นำไปใช้กับทั้งสองและ. ..(ถ้าfindสำรวจ.ซ้ำมันจะจบลงในไดเรกทอรีอีกครั้งและอีกครั้งและอีกครั้งและ ... ) .และ..ไม่ได้เป็นเพียงสัญลักษณ์พิเศษพวกเขาจะปรากฏเป็นรายการไดเรกทอรี
Gilles 'ดังนั้น - หยุดความชั่วร้าย'

5

ไม่มีบรรทัดฐานของอาร์กิวเมนต์บรรทัดคำสั่งก่อนทำการทดสอบ ดังนั้นผลลัพธ์ที่แตกต่างกันขึ้นอยู่กับเส้นทางที่ใช้ (หากมีการเชื่อมโยงที่เกี่ยวข้อง):

cd /tmp
mkdir foo
ln -s foo bar
find /tmp/foo -name foo
find /tmp/bar -name foo

ใน "กรณีของคุณ" การโทรทั้งสองแบบจะให้ผลลัพธ์แบบเดียวกันซึ่งอาจทำให้เกิดความสับสนมากขึ้น คุณสามารถใช้-mindepth 1หากคุณต้องการละเว้นจุดเริ่มต้น (อาจไม่ใช่ POSIX)


-mindepth 1โรงงาน อย่างไรก็ตามฉันยังไม่เข้าใจว่าทำไมfind . -name 'foo'และfind /tmp/foo -name 'foo'ประพฤติแตกต่างกัน
York

2

(gnu) find แสดงการจับคู่ใด ๆ ที่พบภายในพา ธ ที่มีให้กับคำสั่งเนื่องจากมันเริ่มต้นการเปรียบเทียบกับอาร์กิวเมนต์บรรทัดคำสั่งซึ่งลึกลงไปในโครงสร้างไดเรกทอรีจากที่นั่น (ดังนั้น-maxdepth 0จำกัด การทดสอบระดับฐานหรืออาร์กิวเมนต์บรรทัดคำสั่งเท่านั้น-mindepth 1ข้ามอาร์กิวเมนต์บรรทัดคำสั่งตามที่man findอธิบาย) นี่คือเหตุผลที่find /tmp/foo -name 'foo'จะให้ผลการค้นหาที่ตรงกันแม้ว่าไดเรกทอรีนั้นจะว่างเปล่า

find . -name 'foo'ในทางกลับกันจะไม่ให้ผลลัพธ์ใด ๆ เพราะ.(dot) เป็นไฟล์พิเศษที่ทำหน้าที่เหมือน hardlink ไปยัง inode เดียวกันกับ/tmp/fooมันเป็นเหมือนชื่อไฟล์แยก (แม้ว่าพิเศษ) และไม่ใช่ลิงก์สัญลักษณ์หรือนิพจน์ที่อยู่ภายใต้ การขยายชื่อพา ธ โดยเปลือก ดังนั้นการทดสอบครั้งแรกที่ใช้โดยหาข้อโต้แย้ง commandline ในตัวอย่างที่กำหนดจะไม่แสดงการแข่งขันใด ๆ ตั้งแต่แน่นอนไม่ตรงกับรูปแบบชื่อที่กำหนดไว้ใน. -name 'foo'ไม่ไม่/tmp/foo/.เนื่องจากการทดสอบหา-nameรูปแบบที่จะดำเนินการใน basename ของเส้นทางเท่านั้น (ดูman find) .ซึ่งที่นี่อีกครั้งเป็น

ในขณะที่พฤติกรรมนี้อาจไม่ได้รับการคาดหวังหรือปรากฏขึ้นตามสัญชาตญาณของผู้ใช้ (และใช่ฉันมีความสับสนในตอนแรกเช่นกัน) แต่ก็ไม่ได้เป็นข้อบกพร่อง แต่สอดคล้องกับตรรกะและหน้าที่ที่อธิบายไว้ในหน้า man และ info สำหรับ ( gnu) ค้นหา


0

ฉันพยายามแสดงความคิดเห็นคำตอบของ Gilles แต่มันนานเกินไปที่จะแสดงความคิดเห็น ด้วยเหตุนี้ฉันจึงตอบคำถามของฉันเอง

คำอธิบายของ Gilles (และของ Shevek ด้วย) ชัดเจนและสมเหตุสมผล จุดสำคัญในที่นี้คือไม่เพียง แต่findพยายามจับคู่ชื่อไฟล์สำหรับไฟล์ภายในเส้นทางที่กำหนด (ซ้ำ) แต่ยังพยายามจับคู่ชื่อฐานของเส้นทางที่กำหนดด้วยตนเอง

ในทางกลับกันมีหลักฐานว่านี่คือวิธีที่findควรจะทำงานแทนที่จะเป็นข้อผิดพลาดหรือไม่ ในความคิดของฉันมันจะดีกว่าถ้ามันไม่ได้ทำให้ผลลัพธ์ไม่สอดคล้องกันfind .และfind ABSOLUTE-PATHเนื่องจากความไม่สอดคล้องกันทำให้เกิดความสับสนอยู่เสมอและอาจทำให้นักพัฒนาเสียเวลาพยายามที่จะเข้าใจว่า ในกรณีของฉันฉันกำลังเขียนสคริปต์และเส้นทางถูกนำมาจากตัวแปร find $path/. -name 'pattern'ดังนั้นเพื่อให้สคริปต์ของฉันทำงานอย่างถูกต้องสิ่งที่ฉันสามารถคิดในการทำคือการเขียน

ในที่สุดฉันคิดว่าการรับผลลัพธ์ที่สอดคล้องกันfindสามารถทำได้โดยการแทนที่.ด้วยไดเรกทอรีปัจจุบันก่อนดำเนินการต่อ


-1

ไม่มีวัตถุชื่อfooในไดเรกทอรีที่คุณเริ่มการค้นหาที่เกี่ยวข้อง

คุณถูกต้องโดยสมมติว่าgfindเป็นรถเมื่อรายงาน/tmp/fooเมื่อใช้ชื่อแน่นอนเป็นไดเรกทอรีเริ่มต้น

Gfindมีส่วนเบี่ยงเบนต่าง ๆ จากมาตรฐานดูเหมือนว่าคุณจะพบอีกคนหนึ่ง เมื่อคุณชอบวิธีการแก้ปัญหาที่สอดคล้องกับมาตรฐานมากขึ้นผมขอแนะนำว่าเป็นส่วนหนึ่งของsfindschilytools

-nameนำไปใช้กับผลการค้นหาไดเรกทอรี ไม่มีสองกรณีที่คุณกล่าวถึงจะส่งคืนรายการไดเรกทอรีfooจากการreaddir()ดำเนินการ


ฉันสงสัยว่านี่เป็นข้อผิดพลาดเช่นกัน นี่คือผลลัพธ์จากfind --version: find (GNU findutils) 4.4.2 ลิขสิทธิ์ (C) 2007 ซอฟต์แวร์เสรี Foundation, Inc. ใบอนุญาต GPLv3 +: GNU GPL เวอร์ชัน 3 หรือใหม่กว่า < gnu.org/licenses/gpl.html >
York

ฉันตรวจสอบคำสั่งตัวอย่างของคุณด้วยfind(รับรอง POSIX) gfindและsfind; gfindปัญหามีอยู่เพียงเมื่อคุณใช้ บน Linux gfindไม่ได้ติดตั้งภายใต้ชื่อพื้นเมืองของมัน findแต่ภายใต้ชื่อ
schily

3
มันเป็นที่ถูกต้องสำหรับการส่งออกfind /tmp/foo -name foo /tmp/foo(Cc @York) นี่คือลักษณะการทำงานของ OpenBSD find, GNU find, BusyBox find, Solaris findและ POSIX-compliant อื่น ๆfind(“ หลักจะประเมินว่าเป็นจริงถ้าชื่อไฟล์ที่ตรวจสอบรูปแบบการจับคู่(…)” - เมื่อชื่อไฟล์ เป็นสิ่งหนึ่งที่ถูกส่งผ่านเป็นตัวถูกดำเนินการเส้นทางไม่มีreaddirสายที่เกี่ยวข้อง
Gilles 'หยุดความชั่วร้าย'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.