จะได้รับชื่อไฟล์ด้วย Linux 'find' ได้อย่างไร


266

ฉันใช้ค้นหาไฟล์ทั้งหมดในไดเรกทอรีดังนั้นฉันจึงได้รับรายการเส้นทาง อย่างไรก็ตามฉันต้องการชื่อไฟล์เท่านั้น เช่นฉันได้รับ./dir1/dir2/file.txtและฉันต้องการได้รับfile.txt

คำตอบ:


359

ใน GNU findคุณสามารถใช้-printfพารามิเตอร์สำหรับสิ่งนั้นได้เช่น:

find /dir1 -type f -printf "%f\n"

9
เห็นได้ชัดว่าคำตอบแต่มันไม่มีรายละเอียด
Jason McCreary

25
นี่สำหรับ GNU เท่านั้น
Osama F Elias

3
นี้ไม่ได้ทำงานสำหรับฉันเมื่อฉันใช้หลายประเภทไฟล์ (สวิทช์ -o)
Urchin

9
พบ: -printf: ไม่ทราบหลักหรือผู้ประกอบการ
Holms

@Urchin ไม่มีเหตุผลที่ไม่ควรตราบใดที่คุณมีตรรกะที่ถูกต้อง (เช่น-oมีลำดับความสำคัญต่ำกว่าโดยนัย-aดังนั้นคุณมักจะต้องการจัดกลุ่ม-oข้อโต้แย้งของคุณ)
Reinstate Monica โปรด

157

หากการค้นหาของคุณไม่มีตัวเลือก -printf คุณสามารถใช้ basename:

find ./dir1 -type f -exec basename {} \;

17
การอ้างอิงเครื่องหมายอัฒภาคเป็นอีกวิธีในการแก้ปัญหา:... {} ';'
davidchambers

28

ใช้-execdirซึ่งเก็บไฟล์ปัจจุบันไว้โดยอัตโนมัติ{}ตัวอย่างเช่น:

find . -type f -execdir echo '{}' ';'

นอกจากนี้คุณยังสามารถใช้$PWDแทน.(ในบางระบบจะไม่สร้างจุดพิเศษด้านหน้า)

หากคุณยังมีจุดพิเศษอีกทางหนึ่งคุณสามารถเรียกใช้:

find . -type f -execdir basename '{}' ';'

-execdir utility [argument ...] ;

-execdirหลักเป็นเหมือน-execหลักที่มีข้อยกเว้นว่าประโยชน์จะได้รับการดำเนินการจากไดเรกทอรีที่เก็บไฟล์ปัจจุบัน

เมื่อนำมาใช้+แทน;แล้ว{}จะถูกแทนที่ด้วยเป็น pathnames มากที่สุดเท่าที่เป็นไปได้สำหรับการภาวนาของอาคารแต่ละ มันจะพิมพ์ชื่อไฟล์ทั้งหมดในบรรทัดเดียว


ฉันได้รับแทน./filename filenameขึ้นอยู่กับความต้องการของคุณอาจจะดีหรือไม่ดีก็ได้
user276648

@ user276648 ลองกับแทน$PWD .
kenorb

24

หากคุณใช้ GNU พบ

find . -type f -printf "%f\n"

หรือคุณสามารถใช้ภาษาการเขียนโปรแกรมเช่น Ruby (1.9+)

$ ruby -e 'Dir["**/*"].each{|x| puts File.basename(x)}'

หากคุณนึกถึงวิธีทุบตี (อย่างน้อย 4)

shopt -s globstar
for file in **; do echo ${file##*/}; done

ฉันได้รับแรงบันดาลใจจากคำตอบของคุณเพื่อช่วย sleske: serverfault.com/a/745968/329412
Nolwennig

11

หากคุณต้องการดำเนินการบางอย่างกับชื่อไฟล์เท่านั้นการใช้basenameอาจยาก

เช่นนี้

find ~/clang+llvm-3.3/bin/ -type f -exec echo basename {} \; 

จะเพียงแค่สะท้อน/my/found/pathชื่อฐาน ไม่ใช่สิ่งที่เราต้องการหากเราต้องการดำเนินการกับชื่อไฟล์

แต่คุณสามารถxargsส่งออก ตัวอย่างเช่นเพื่อฆ่าไฟล์ใน dir ตามชื่อใน dir อื่น:

cd dirIwantToRMin;
find ~/clang+llvm-3.3/bin/ -type f -exec basename {} \; | xargs rm

dont echo -find ~/clang+llvm-3.3/bin/ -type f -exec basename {} \;
commonpike


1

-execและ-execdirช้าxargsเป็นกษัตริย์

$ alias f='time find /Applications -name "*.app" -type d -maxdepth 5'; \
f -exec basename {} \; | wc -l; \
f -execdir echo {} \; | wc -l; \
f -print0 | xargs -0 -n1 basename | wc -l; \
f -print0 | xargs -0 -n1 -P 8 basename | wc -l; \
f -print0 | xargs -0 basename | wc -l

     139
    0m01.17s real     0m00.20s user     0m00.93s system
     139
    0m01.16s real     0m00.20s user     0m00.92s system
     139
    0m01.05s real     0m00.17s user     0m00.85s system
     139
    0m00.93s real     0m00.17s user     0m00.85s system
     139
    0m00.88s real     0m00.12s user     0m00.75s system

xargsการขนานของมันก็ช่วยได้เช่นกัน

ขำพอฉันไม่สามารถอธิบายกรณีสุดท้ายxargsโดยไม่-n1ได้ มันให้ผลลัพธ์ที่ถูกต้องและเร็วที่สุด¯\_(ツ)_/¯

( basenameใช้เวลาเพียง 1 พา ธ อาร์กิวเมนต์ แต่xargsจะส่งทั้งหมด (จริง ๆ 5,000) โดย-n1ไม่ทำงานบน linux และ openbsd, macOS เท่านั้น ... )

ตัวเลขที่ใหญ่กว่าบางส่วนจากระบบ linux เพื่อดูว่ามีประโยชน์อย่างไร-execdirแต่ยังช้ากว่าแบบขนานมากxargs:

$ alias f='time find /usr/ -maxdepth 5 -type d'
$ f -exec basename {} \; | wc -l; \
f -execdir echo {} \; | wc -l; \
f -print0 | xargs -0 -n1 basename | wc -l; \
f -print0 | xargs -0 -n1 -P 8 basename | wc -l

2358
    3.63s real     0.10s user     0.41s system
2358
    1.53s real     0.05s user     0.31s system
2358
    1.30s real     0.03s user     0.21s system
2358
    0.41s real     0.03s user     0.25s system

1
จุดข้อมูลอีกหนึ่งจุด: บน openbsd ในระยะยาวfindมันจะ-execdirกลายเป็นวิธีที่เร็วที่สุดเนื่องจากการสร้างกระบวนการใหม่เป็นการดำเนินการที่ค่อนข้างแพง
ลบ

1

สุจริตbasenameและdirnameการแก้ปัญหาง่ายขึ้น แต่คุณสามารถลองดูได้ที่:

find . -type f | grep -oP "[^/]*$"

หรือ

find . -type f | rev | cut -d '/' -f1 | rev

หรือ

find . -type f | sed "s/.*\///"

0

ตามที่คนอื่น ๆ ได้ชี้ให้เห็นคุณสามารถรวมfindและbasenameแต่โดยค่าเริ่มต้นbasenameโปรแกรมจะทำงานบนเส้นทางเดียวในแต่ละครั้งดังนั้นการปฏิบัติการจะต้องเปิดตัวครั้งเดียวสำหรับแต่ละเส้นทาง (ใช้อย่างใดอย่างหนึ่งfind ... -execหรือfind ... | xargs -n 1) ซึ่งอาจช้า

หากคุณใช้-aตัวเลือกbasenameนี้จะสามารถรับชื่อไฟล์หลาย ๆ ไฟล์ในการเรียกใช้ครั้งเดียวซึ่งหมายความว่าคุณสามารถใช้xargsโดยไม่ต้อง-n 1เพื่อจัดกลุ่มเส้นทางเข้าด้วยกันเป็นจำนวนการเรียกใช้ที่น้อยกว่าbasenameซึ่งควรมีประสิทธิภาพมากกว่า

ตัวอย่าง:

find /dir1 -type f -print0 | xargs -0 basename -a

ที่นี่ฉันได้รวม-print0และ-0(ซึ่งควรใช้ร่วมกัน) เพื่อจัดการกับช่องว่างใด ๆ ภายในชื่อของไฟล์และไดเรกทอรี

นี่คือการเปรียบเทียบเวลาระหว่างxargs basename -aและxargs -n1 basenameเวอร์ชัน (เพื่อประโยชน์ในการเปรียบเทียบแบบใกล้เคียงกันการกำหนดเวลาที่รายงานในที่นี้คือหลังจากการเรียกใช้ dummy ครั้งแรกเพื่อให้ทั้งคู่เสร็จสิ้นหลังจากที่ข้อมูลเมตาของไฟล์ถูกคัดลอกไปยังแคช I / O แล้ว) cksumในทั้งสองกรณีเพียงเพื่อแสดงให้เห็นว่าผลลัพธ์ที่เป็นอิสระจากวิธีการที่ใช้

$ time sh -c 'find /usr/lib -type f -print0 | xargs -0 basename -a | cksum'
2532163462 546663

real    0m0.063s
user    0m0.058s
sys 0m0.040s

$ time sh -c 'find /usr/lib -type f -print0 | xargs -0 -n 1 basename | cksum' 
2532163462 546663

real    0m14.504s
user    0m12.474s
sys 0m3.109s

อย่างที่คุณเห็นมันเร็วกว่าการหลีกเลี่ยงการเปิดตัวbasenameทุกครั้ง


การอ่านคำตอบจาก @minusf ให้ละเอียดยิ่งขึ้นฉันเห็นว่าใน Mac basenameจะยอมรับชื่อไฟล์หลาย ๆ ไฟล์โดยไม่จำเป็นต้องมีอาร์กิวเมนต์บรรทัดคำสั่งเพิ่มเติมใด ๆ การใช้-aที่นี่อยู่บน Linux ( basename --versionบอกฉันbasename (GNU coreutils) 8.28)
alaniwi

-3

ฉันได้พบวิธีแก้ปัญหา (ในหน้า makandracards) ที่ให้ชื่อไฟล์ใหม่ล่าสุด:

ls -1tr * | tail -1

(ขอบคุณไปที่ Arne Hartherz)

ฉันใช้มันเพื่อcp:

cp $(ls -1tr * | tail -1) /tmp/

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