รับ dirname / ชื่อไฟล์ล่าสุดในอาร์กิวเมนต์พา ธ ของไฟล์ใน Bash


228

ฉันกำลังพยายามเขียนเบ็ด post-commit สำหรับ SVN ซึ่งโฮสต์บนเซิร์ฟเวอร์การพัฒนาของเรา เป้าหมายของฉันคือพยายามตรวจสอบสำเนาของโปรเจ็กต์ที่กำหนดไปยังไดเร็กทอรีที่โฮสต์บนเซิร์ฟเวอร์โดยอัตโนมัติ อย่างไรก็ตามฉันต้องสามารถอ่านเฉพาะไดเรกทอรีสุดท้ายในสตริงไดเรกทอรีที่ส่งไปยังสคริปต์เพื่อชำระเงินไปยังไดเรกทอรีย่อยเดียวกันที่โฮสต์โครงการของเรา

ตัวอย่างเช่นถ้าฉันทำ SVN ส่งมอบโครงการ "ตัวอย่าง" สคริปต์ของฉันจะได้รับ "/ usr / local / svn / repos / example" เป็นอาร์กิวเมนต์แรก ฉันต้องได้รับ "ตัวอย่าง" จากจุดสิ้นสุดของสตริงแล้วต่อกับสตริงอื่นเพื่อให้ฉันสามารถเช็คเอาต์ที่ "/ server / root / example" และดูการเปลี่ยนแปลงที่เกิดขึ้นทันที

คำตอบ:


344

basename ลบคำนำหน้าไดเรกทอรีของเส้นทาง:

$ basename /usr/local/svn/repos/example
example
$ echo "/server/root/$(basename /usr/local/svn/repos/example)"
/server/root/example

2
basename เป็นสิ่งที่ฉันกำลังมองหา วิธีการรับ basename ของการโต้แย้งเก็บไว้ในตัวแปรอย่างไร เช่นSUBDIR="/path/to/whatever/$(basename $1)"
TJ L

5
@ tj111: เสียงเหมือนจะไม่มี$1หรือ$1เป็นที่ว่างเปล่า

โชคไม่ดีถ้าคุณตัดคำสั่ง basename ไม่ใช่ความคิดที่ดี สิ่งที่ต้องจำไว้
dtc

98

วิธีการต่อไปนี้สามารถใช้เพื่อรับพา ธ ใด ๆ ของชื่อพา ธ :

some_path=a/b/c
echo $(basename $some_path)
echo $(basename $(dirname $some_path))
echo $(basename $(dirname $(dirname $some_path)))

เอาท์พุท:

c
b
a

1
ไม่ทำงานกับเส้นทางที่มีช่องว่าง ... คุณสามารถเอาชนะมันด้วยเครื่องหมายคำพูด ... ซึ่งทำงานได้อย่างใดecho "$(basename "$(dirname "$pathname")")"
Ray Foss

75

Bash สามารถรับส่วนสุดท้ายของเส้นทางโดยไม่ต้องเรียกภายนอกbasename:

subdir="/path/to/whatever/${1##*/}"

2
ใน Mac ของฉันการใช้ซับสตริงสัญกรณ์เป็นมากกว่าลำดับความสำคัญเร็วกว่า dirname / basename สำหรับกรณีที่คุณกำลังทำบางสิ่งเล็กน้อยกับไฟล์สองสามพันไฟล์
จอร์จ

1
d=/home/me/somefolder;subdir="/$d/${1##*/}"ฉันลงเอยด้วยบางสิ่งบางอย่างเช่น//home/me/somefolder//$ d มาจากลูปfor d in $(find $SOMEFOLDER -maxdepth 1 -type d);การใช้subdir=$(basename $d)ผลงานตามที่คาดไว้
Buttle Butkus

1
@ButtleButkus: คุณควรใช้whileแทนforการวนซ้ำของผลลัพธ์find( find -print0 | xargs -0ดีกว่า) หรือใช้ globbing: for d in $SOMEFOLDER/*/(เครื่องหมายทับสุดท้ายทำงานเหมือน-type d- คุณสามารถใช้**ใน Bash 4 สำหรับการเรียกซ้ำหากคุณshopt -s globstarแต่ข้อความ "รายการอาร์กิวเมนต์ยาวเกินไป" คือ เป็นไปได้) โปรดทราบว่า${1}ส่วนของคำสั่งแสดงถึงอาร์กิวเมนต์แรกของสคริปต์หรือฟังก์ชั่น คุณอาจจำเป็นต้องใช้${d##*/}หรือข้อกำหนดตัวแปรหรือข้อโต้แย้งอื่น ๆ หรือตรวจสอบให้แน่ใจว่ามีการส่งผ่านข้อโต้แย้ง$1
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

2
@DennisWilliamson ขอบคุณมากสำหรับการแบ่งปัน สำหรับผู้อ่านในอนาคตที่เริ่มสงสัยว่าทำไมมันไม่ทำงานหรือฉันเป็นคนเดียวที่โง่ที่นี่ 😂 คำตอบข้างต้นสันนิษฐานว่ามี$1 the path from which last component is to be taken outฉันพลาดส่วนนั้น กรณีการใช้งานของฉัน: target_path='/home/user/dir1/dir2/dir3/'; target_path="${target_path%/}"; last_component=${target_path##*/}; echo $last_component- ทำงาน😉
Vinay Vissh

2
ดูที่นี่สำหรับคำอธิบายของวิธีการและเหตุผลที่ ${1##*/}ผลงาน: unix.stackexchange.com/a/171786/15070
แมทธิว
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.