ฉันใช้ค้นหาไฟล์ทั้งหมดในไดเรกทอรีดังนั้นฉันจึงได้รับรายการเส้นทาง อย่างไรก็ตามฉันต้องการชื่อไฟล์เท่านั้น เช่นฉันได้รับ./dir1/dir2/file.txt
และฉันต้องการได้รับfile.txt
ฉันใช้ค้นหาไฟล์ทั้งหมดในไดเรกทอรีดังนั้นฉันจึงได้รับรายการเส้นทาง อย่างไรก็ตามฉันต้องการชื่อไฟล์เท่านั้น เช่นฉันได้รับ./dir1/dir2/file.txt
และฉันต้องการได้รับfile.txt
คำตอบ:
ใน GNU find
คุณสามารถใช้-printf
พารามิเตอร์สำหรับสิ่งนั้นได้เช่น:
find /dir1 -type f -printf "%f\n"
-o
มีลำดับความสำคัญต่ำกว่าโดยนัย-a
ดังนั้นคุณมักจะต้องการจัดกลุ่ม-o
ข้อโต้แย้งของคุณ)
หากการค้นหาของคุณไม่มีตัวเลือก -printf คุณสามารถใช้ basename:
find ./dir1 -type f -exec basename {} \;
... {} ';'
ใช้-execdir
ซึ่งเก็บไฟล์ปัจจุบันไว้โดยอัตโนมัติ{}
ตัวอย่างเช่น:
find . -type f -execdir echo '{}' ';'
นอกจากนี้คุณยังสามารถใช้$PWD
แทน.
(ในบางระบบจะไม่สร้างจุดพิเศษด้านหน้า)
หากคุณยังมีจุดพิเศษอีกทางหนึ่งคุณสามารถเรียกใช้:
find . -type f -execdir basename '{}' ';'
-execdir utility [argument ...] ;
-execdir
หลักเป็นเหมือน-exec
หลักที่มีข้อยกเว้นว่าประโยชน์จะได้รับการดำเนินการจากไดเรกทอรีที่เก็บไฟล์ปัจจุบัน
เมื่อนำมาใช้+
แทน;
แล้ว{}
จะถูกแทนที่ด้วยเป็น pathnames มากที่สุดเท่าที่เป็นไปได้สำหรับการภาวนาของอาคารแต่ละ มันจะพิมพ์ชื่อไฟล์ทั้งหมดในบรรทัดเดียว
./filename
filename
ขึ้นอยู่กับความต้องการของคุณอาจจะดีหรือไม่ดีก็ได้
$PWD
.
หากคุณใช้ 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
หากคุณต้องการดำเนินการบางอย่างกับชื่อไฟล์เท่านั้นการใช้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
find ~/clang+llvm-3.3/bin/ -type f -exec basename {} \;
สำหรับ mac (BSD find
) ให้ใช้:
find /dir1 -type f -exec basename {} \;
-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
find
มันจะ-execdir
กลายเป็นวิธีที่เร็วที่สุดเนื่องจากการสร้างกระบวนการใหม่เป็นการดำเนินการที่ค่อนข้างแพง
สุจริตbasename
และdirname
การแก้ปัญหาง่ายขึ้น แต่คุณสามารถลองดูได้ที่:
find . -type f | grep -oP "[^/]*$"
หรือ
find . -type f | rev | cut -d '/' -f1 | rev
หรือ
find . -type f | sed "s/.*\///"
ตามที่คนอื่น ๆ ได้ชี้ให้เห็นคุณสามารถรวม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
ทุกครั้ง
basename
จะยอมรับชื่อไฟล์หลาย ๆ ไฟล์โดยไม่จำเป็นต้องมีอาร์กิวเมนต์บรรทัดคำสั่งเพิ่มเติมใด ๆ การใช้-a
ที่นี่อยู่บน Linux ( basename --version
บอกฉันbasename (GNU coreutils) 8.28
)
ฉันได้พบวิธีแก้ปัญหา (ในหน้า makandracards) ที่ให้ชื่อไฟล์ใหม่ล่าสุด:
ls -1tr * | tail -1
(ขอบคุณไปที่ Arne Hartherz)
ฉันใช้มันเพื่อcp
:
cp $(ls -1tr * | tail -1) /tmp/