รับชื่อไดเรกทอรีปัจจุบัน (ไม่มีพา ธ เต็ม) ในสคริปต์ Bash


819

ฉันจะได้รับเพียงแค่ชื่อไดเรกทอรีการทำงานปัจจุบันในสคริปต์ทุบตีหรือดีกว่าเพียงแค่คำสั่ง terminal

pwdให้เส้นทางแบบเต็มของไดเรกทอรีการทำงานปัจจุบันเช่น/opt/local/binแต่ฉันต้องการเท่านั้นbin


1
ด้วยเส้นทางแบบเต็มดู: การเดินทางไดเรกทอรีแหล่งที่มาของสคริปต์ทุบตีจากภายใน
kenorb

คำตอบ:


1115

ไม่จำเป็นต้องมีชื่อไฟล์และโดยเฉพาะอย่างยิ่งไม่จำเป็นต้องใช้ subshell ที่เรียกใช้ pwd (ซึ่งเป็นการเพิ่มการแยกแบบพิเศษและราคาแพง ); เชลล์สามารถทำได้ภายในโดยใช้การขยายพารามิเตอร์ :

result=${PWD##*/}          # to assign to a variable

printf '%s\n' "${PWD##*/}" # to print to stdout
                           # ...more robust than echo for unusual names
                           #    (consider a directory named -e or -n)

printf '%q\n' "${PWD##*/}" # to print to stdout, quoted for use as shell input
                           # ...useful to make hidden characters readable.

โปรดทราบว่าหากคุณใช้เทคนิคนี้ในสถานการณ์อื่น ๆ (ไม่ใช่PWDแต่ตัวแปรอื่น ๆ ที่มีชื่อไดเรกทอรี) คุณอาจต้องตัดเครื่องหมายสแลชต่อท้าย ด้านล่างใช้การสนับสนุน extglobของ bash เพื่อทำงานแม้จะมีเครื่องหมายทับหลาย ๆ

dirname=/path/to/somewhere//
shopt -s extglob           # enable +(...) glob syntax
result=${dirname%%+(/)}    # trim however many trailing slashes exist
result=${result##*/}       # remove everything before the last / that still remains
printf '%s\n' "$result"

อีกทางเลือกหนึ่งโดยไม่มีextglob:

dirname="/path/to/somewhere//"
result="${dirname%"${dirname##*[!/]}"}" # extglob-free multi-trailing-/ trim
result="${result##*/}"                  # remove everything before the last /

31
อะไรคือความแตกต่างระหว่าง $ {PWD ## * /} และ $ PWD?
Mr_Chimp

24
@Mr_Chimp ในอดีตคือการดำเนินการขยายตัวของพารามิเตอร์ซึ่งตัดส่วนที่เหลือของไดเรกทอรีเพื่อให้เฉพาะชื่อไฟล์ ฉันมีลิงก์ในคำตอบสำหรับเอกสารประกอบเกี่ยวกับการขยายพารามิเตอร์ อย่าลังเลที่จะปฏิบัติตามหากคุณมีข้อสงสัย
ชาร์ลส์ดัฟฟี่

24
@stefgosselin $PWDเป็นชื่อของตัวแปร bash ในตัว; ชื่อตัวแปรในตัวทั้งหมดอยู่ในตัวพิมพ์ใหญ่ทั้งหมด (เพื่อแยกความแตกต่างจากตัวแปรท้องถิ่นซึ่งควรมีอักขระตัวพิมพ์เล็กอย่างน้อยหนึ่งตัว) result=${PWD#*/}ไม่ได้ประเมิน/full/path/to/directory; แต่จะแยกเฉพาะองค์ประกอบแรกpath/to/directoryเท่านั้น การใช้#อักขระสองตัวทำให้รูปแบบจับคู่โลภจับคู่กับตัวละครให้ได้มากที่สุด อ่านหน้าการขยายพารามิเตอร์เชื่อมโยงในคำตอบเพิ่มเติม
ชาร์ลส์ดัฟฟี่

5
โปรดทราบว่าเอาต์พุตจากpwdอาจไม่เหมือนกันเสมอกับค่าของ$PWDเนื่องจากความยุ่งยากที่เกิดจากcdไอเอ็นจีผ่านลิงก์สัญลักษณ์ไม่ว่าทุบตีมีการ-o physicalตั้งค่าตัวเลือกและอื่น ๆ สิ่งนี้เคยเป็นสิ่งที่น่ารังเกียจเป็นพิเศษเกี่ยวกับการจัดการไดเรกทอรีอัตโนมัติซึ่งการบันทึกเส้นทางทางกายภาพแทนที่จะเป็นตรรกะจะสร้างเส้นทางที่ถ้าใช้จะอนุญาตให้ตัวนับอัตโนมัติลงจากไดเรกทอรีที่ใช้งานอยู่
Alex North-Keys

3
ดี! ใครบางคนควรเขียนหนังสือให้กับคนที่ได้รับเปลือก Borne อย่างฉัน อาจจะมีหนึ่งออกมี มันอาจจะมี crotchity SYS เก่า ADMIN บอกว่าสิ่งที่ชอบ "กลับมาในวันที่ฉันเราshและcshและหากคุณต้องการที่ปุ่ม Backspace ในการทำงานคุณต้องอ่านทั้งsttyหน้าคนและเราชอบมัน!"
Red Cricket

335

ใช้basenameโปรแกรม สำหรับกรณีของคุณ:

% basename "$PWD"
bin

20
นี่ไม่ใช่จุดประสงค์ของbasenameโปรแกรมใช่หรือไม่ มีอะไรผิดปกติกับคำตอบนี้นอกจากคำพูดที่หายไป?
Nacht - Reinstate Monica

11
@Nacht basenameย่อมเป็นโปรแกรมภายนอกว่าจะเป็นสิ่งที่ถูกต้อง แต่การทำงานใด ๆโปรแกรมภายนอกสำหรับทุบตีสิ่งที่สามารถทำออกจากกล่องโดยใช้ฟังก์ชันในตัวเดียวคือโง่ที่เกิดขึ้นส่งผลกระทบต่อผลการดำเนินงาน ( fork(), execve(), wait()ฯลฯ ) สำหรับ ไม่มีเหตุผล.
Charles Duffy

3
... นอกจากนี้การคัดค้านของฉันbasenameที่นี่ใช้ไม่ได้dirnameเพราะdirnameมีฟังก์ชั่นที่${PWD##*/}ไม่ได้ - แปลงสตริงที่ไม่มีเครื่องหมายทับเพื่อ.เป็นต้น ดังนั้นในขณะที่ใช้dirnameเป็นเครื่องมือภายนอกมีค่าใช้จ่ายด้านประสิทธิภาพ แต่ก็มีฟังก์ชั่นที่ช่วยชดเชยได้เหมือนกัน
Charles Duffy

4
@LouisMaddox บิตของ kibitzing: functionคำหลัก POSIX- เข้ากันไม่ได้โดยไม่ต้องเพิ่มค่าใด ๆ ในไวยากรณ์มาตรฐานและการขาดคำพูดจะสร้างข้อบกพร่องในชื่อไดเรกทอรีที่มีช่องว่าง wdexec() { "./$(basename "$PWD")"; }อาจเป็นทางเลือกที่ดีกว่า
ชาร์ลส์ดัฟฟี่

28
แน่นอนว่าการใช้งานbasenameมีประสิทธิภาพน้อยกว่า แต่มันอาจมีประสิทธิภาพมากกว่าในแง่ของประสิทธิภาพการทำงานของนักพัฒนาเพราะมันง่ายต่อการจดจำเมื่อเปรียบเทียบกับไวยากรณ์ bash ที่น่าเกลียด ดังนั้นถ้าคุณจำมันไปได้เลย มิฉะนั้นbasenameทำงานได้เช่นกัน มีใครเคยต้องปรับปรุง "ประสิทธิภาพ" ของสคริปต์ทุบตีและทำให้มีประสิทธิภาพมากขึ้นหรือไม่
usr

139
$ echo "${PWD##*/}"

​​​​​


2
@jocap ... ยกเว้นว่าการใช้งานของก้องที่นั่นโดยไม่ต้องอ้างอาร์กิวเมนต์เป็นผิด หากชื่อไดเรกทอรีมีช่องว่างหลายช่องหรืออักขระแท็บชื่อนั้นจะถูกยุบลงในช่องว่างเดียว หากมีอักขระตัวแทนจะมีการขยาย echo "${PWD##*/}"การใช้งานที่ถูกต้องจะเป็น
Charles Duffy

9
@ โจแคป ... เช่นกันฉันไม่แน่ใจว่า "echo" เป็นคำตอบที่ถูกต้องตามกฎหมาย คำถามคือทำอย่างไรจึงจะได้คำตอบไม่ใช่พิมพ์คำตอบอย่างไร หากเป้าหมายคือการกำหนดให้ตัวแปรตัวอย่างเช่นname=${PWD##*/}จะถูกต้องและname=$(echo "${PWD##*/}")จะผิด (ในแง่ที่ไร้ประสิทธิภาพ)
Charles Duffy

24

คุณสามารถใช้การรวมกันของ pwd และ basename เช่น

#!/bin/bash

CURRENT=`pwd`
BASENAME=`basename "$CURRENT"`

echo "$BASENAME"

exit;

18
ได้โปรดไม่ใช่ backticks สร้าง subshell (ดังนั้นเป็นการแยก fork) - และในกรณีนี้คุณใช้มันสองครั้ง ! [ในฐานะเพิ่มเติมโวหารแม้ว่าโวหารโวหาร - ชื่อตัวแปรควรเป็นตัวพิมพ์เล็กยกเว้นว่าคุณส่งออกไปยังสภาพแวดล้อมหรือเป็นกรณีพิเศษที่สงวนไว้]
ชาร์ลส์ดัฟฟี่

11
และในฐานะรูปแบบที่สองการเล่นลิ้นการเล่นลิ้นควรจะแทนที่ด้วย $ () ยังคงส้อม แต่อ่านได้มากขึ้นและมีความจำเป็นน้อยกว่า excaping
Jeremy Wall

1
ตามแบบแผนตัวแปรสภาพแวดล้อม (PATH, EDITOR, SHELL, ... ) และตัวแปรเชลล์ภายใน (BASH_VERSION, RANDOM, ... ) ได้รับการแปลงเป็นตัวพิมพ์ใหญ่ทั้งหมด ชื่อตัวแปรอื่น ๆ ทั้งหมดควรเป็นตัวพิมพ์เล็ก เนื่องจากชื่อตัวแปรคำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ข้อตกลงนี้จะหลีกเลี่ยงการทับตัวแปรด้านสิ่งแวดล้อมและภายในโดยไม่ตั้งใจ
Rany Albeg Wein

2
ขึ้นอยู่กับการประชุม หนึ่งสามารถยืนยันว่าตัวแปรเชลล์ทั้งหมดเป็นตัวแปรสภาพแวดล้อม (ขึ้นอยู่กับเชลล์) และตัวแปรเชลล์ทั้งหมดควรอยู่ใน all-caps บางคนอาจโต้แย้งตามขอบเขต - ตัวแปรทั่วโลกเป็นตัวพิมพ์ใหญ่ทั้งหมดในขณะที่ตัวแปรในตัวเครื่องเป็นตัวพิมพ์เล็ก อีกคนหนึ่งอาจแย้งว่าการสร้างตัวแปร all-uppercase เพิ่มเลเยอร์พิเศษของความเปรียบต่างกับคำสั่ง littering shell script ซึ่งทั้งหมดเป็นตัวพิมพ์เล็ก แต่มีความหมาย ฉันอาจหรือไม่ / เห็นด้วยกับข้อโต้แย้งใด ๆ แต่ฉันเห็นการใช้งานทั้งสามข้อ :)
dannysauer

17

วิธีการเกี่ยวกับ grep:

pwd | grep -o '[^/]*$'

จะไม่แนะนำเพราะมันดูเหมือนว่าจะได้รับข้อมูลบางสีในขณะที่pwd | xargs basenameไม่ได้ .. อาจจะสำคัญไม่ว่า แต่คำตอบอื่น ๆ ที่เป็นที่เรียบง่ายและสอดคล้องกันมากขึ้นในสภาพแวดล้อม
davidhq

8

ฉันชอบคำตอบที่เลือก (Charles Duffy) แต่ระวังถ้าคุณอยู่ใน dir symlinked และคุณต้องการชื่อของ dir เป้าหมาย น่าเสียดายที่ฉันไม่คิดว่าสามารถทำได้ในนิพจน์การขยายพารามิเตอร์เดียวบางทีฉันเข้าใจผิด สิ่งนี้น่าจะใช้ได้:

target_PWD=$(readlink -f .)
echo ${target_PWD##*/}

หากต้องการดูสิ่งนี้การทดลอง:

cd foo
ln -s . bar
echo ${PWD##*/}

รายงาน "แถบ"

dirname

ในการแสดงไดเร็กทอรีชั้นนำของพา ธ (โดยไม่เกิดการแยก fork-exec ของ / usr / bin / dirname):

echo ${target_PWD%/*}

เช่นจะแปลง foo / bar / baz -> foo / bar


5
น่าเสียดายที่readlink -fเป็นส่วนขยายของ GNU และไม่สามารถใช้ได้ใน BSD (รวมถึง OS X)
Xiong Chiamiov

8
echo "$PWD" | sed 's!.*/!!'

หากคุณกำลังใช้เชลล์เป้าหมายหรือ${PWD##*/}ไม่พร้อมใช้งาน


4
FYI ${PWD##*/}คือ POSIX sh - ทุกทันสมัย ​​/ bin / sh (รวมถึงเส้นประเถ้า ฯลฯ ) สนับสนุน หากต้องการชมบอร์นที่เกิดขึ้นจริงในกล่องล่าสุดคุณจะต้องอยู่ในระบบโซลาริสเก่า ๆ นอกเหนือจากนั้น - echo "$PWD"; การไม่ใส่เครื่องหมายอัญประกาศนำไปสู่ข้อบกพร่อง (หากชื่อไดเรกทอรีมีช่องว่างอักขระตัวแทน ฯลฯ )
ชาร์ลส์ดัฟฟี่

1
ใช่ฉันใช้ระบบโซลาริสโบราณ ฉันได้อัปเดตคำสั่งเพื่อใช้เครื่องหมายคำพูด
anomal


5

น่าแปลกที่ไม่มีใครพูดถึงตัวเลือกนี้ที่ใช้คำสั่ง bash ในตัวเท่านั้น:

i="$IFS";IFS='/';set -f;p=($PWD);set +f;IFS="$i";echo "${p[-1]}"

ในฐานะโบนัสที่เพิ่มเข้ามาคุณสามารถได้รับชื่อของไดเรกทอรีหลักด้วย:

[ "${#p[@]}" -gt 1 ] && echo "${p[-2]}"

สิ่งเหล่านี้จะใช้ได้กับ Bash 4.3-alpha หรือใหม่กว่า


ประหลาดใจ แค่ล้อเล่น แต่คนอื่นสั้นกว่าและจำง่ายกว่ามาก
codewandler

นี่เป็นเรื่องตลกมาก = P ไม่เคยได้ยินเรื่อง $ IFS (ตัวคั่นเขตข้อมูลภายใน) มาก่อนหน้านี้ ทุบตีของฉันเก่าเกินไป แต่สามารถทดสอบได้ที่นี่: jdoodle.com/test-bash-shell-script-online
Stan Kurdziel

5

ใช้:

basename "$PWD"

หรือ

IFS=/ 
var=($PWD)
echo ${var[-1]} 

หมุน Internal Filename Separator (IFS) กลับไปเป็นช่องว่าง

IFS= 

มีช่องว่างหนึ่งช่องหลังจาก IFS


4
basename $(pwd)

หรือ

echo "$(basename $(pwd))"

2
ต้องการคำพูดมากขึ้นมีความถูกต้อง - มันคือการส่งออกของpwdเป็นสตริงแยกและ glob basenameขยายตัวก่อนที่จะถูกส่งผ่านไปยัง
Charles Duffy

ดังนั้นbasename "$(pwd)"- แต่ที่ไม่มีประสิทธิภาพมากเมื่อเทียบกับเพียงbasename "$PWD"ซึ่งเป็นตัวเองที่ไม่มีประสิทธิภาพเทียบกับการใช้การขยายตัวพารามิเตอร์แทนการโทรศัพท์basenameที่ทุกคน
Charles Duffy

4

สำหรับจ๊อกกี้พบที่นั่นเช่นฉัน:

find $PWD -maxdepth 0 -printf "%f\n"

เปลี่ยน$PWDไป"$PWD"เป็นจัดการชื่อไดเรกทอรีผิดปกติอย่างถูกต้อง
Charles Duffy

3

ฉันมักจะใช้สิ่งนี้ในสคริปต์ sh

SCRIPTSRC=`readlink -f "$0" || echo "$0"`
RUN_PATH=`dirname "${SCRIPTSRC}" || echo .`
echo "Running from ${RUN_PATH}"
...
cd ${RUN_PATH}/subfolder

คุณสามารถใช้สิ่งนี้เพื่อทำสิ่งต่าง ๆ โดยอัตโนมัติ ...


readlink: illegal option -- f usage: readlink [-n] [file ...]


1

เพียงใช้:

pwd | xargs basename

หรือ

basename "`pwd`"

2
นี่เป็นคำตอบที่แย่กว่าทุกครั้งที่ Arkady ให้ไว้ก่อนหน้านี้ ( stackoverflow.com/a/1371267/14122 ): xargsformultion ไม่มีประสิทธิภาพและบักกี้เมื่อชื่อไดเรกทอรีมีการขึ้นบรรทัดใหม่หรืออักขระเครื่องหมายคำพูดและสูตรที่สองเรียกpwdคำสั่ง ในเชลล์ย่อยแทนที่จะดึงผลลัพธ์เดียวกันผ่านการขยายตัวแปรในตัว
Charles Duffy

@CharlesDuffy อย่างไรก็ตามเป็นคำตอบที่ถูกต้องและเป็นประโยชน์สำหรับคำถามนี้
bachph

1

หากคุณต้องการดูเฉพาะไดเรกทอรีปัจจุบันในภูมิภาคของ bash prompt คุณสามารถแก้ไข.bashrcไฟล์~ได้ เปลี่ยน\wเป็น\Wในบรรทัด:

PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '

วิ่ง source ~/.bashrcและจะแสดงเฉพาะชื่อไดเรกทอรีในพื้นที่พรอมต์

Ref: /superuser/60555/show-only-current-directory-name-not-full-path-on-bash-prompt


0

คุณสามารถใช้ยูทิลิตี้ basename ซึ่งจะลบคำนำหน้าใด ๆ ที่ลงท้ายด้วย / และคำต่อท้าย (หากมีอยู่ในสตริง) จากสตริงและพิมพ์ผลลัพธ์บนเอาต์พุตมาตรฐาน

$basename <path-of-directory>

0

ฉันชอบที่จะใช้gbasenameซึ่งเป็นส่วนหนึ่งของ coreutils GNU


FYI มันไม่ได้มาพร้อมกับการกระจาย Ubuntu ของฉัน
Katastic Voyage

@KatasticVoyage ก็มีบน Ubuntu ก็เรียกว่าเพียงแค่basenameมี โดยทั่วไปจะเรียกเฉพาะgbasenameใน MacOS และแพลตฟอร์มอื่น ๆ ที่มาพร้อมกับชื่อที่ไม่ใช่ GNU
Charles Duffy

-1

คำสั่งต่อไปนี้จะส่งผลให้พิมพ์ไดเรกทอรีทำงานปัจจุบันของคุณในสคริปต์ทุบตี

pushd .
CURRENT_DIR="`cd $1; pwd`"
popd
echo $CURRENT_DIR

2
(1) ฉันไม่แน่ใจว่าที่ใดที่เรามั่นใจว่ารายการอาร์กิวเมนต์เป็นเช่นนั้นซึ่ง$1จะมีไดเรกทอรีทำงานปัจจุบัน (2) pushdและpopdไม่มีจุดประสงค์ที่นี่เพราะสิ่งที่อยู่ภายใน backticks จะทำใน subshell - ดังนั้นมันไม่สามารถส่งผลกระทบต่อไดเรกทอรีของเปลือกแม่เริ่มต้นด้วย (3) การใช้"$(cd "$1"; pwd)"จะเป็นทั้งอ่านง่ายและยืดหยุ่นกับชื่อไดเรกทอรีที่มีช่องว่าง
Charles Duffy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.