ฉันจะรู้ชื่อไฟล์สคริปต์ในสคริปต์ Bash ได้อย่างไร


605

ฉันจะกำหนดชื่อของไฟล์สคริปต์ Bash ภายในสคริปต์ได้อย่างไร

เช่นถ้าสคริปต์ของฉันอยู่ในไฟล์runme.shฉันจะทำให้มันแสดงข้อความ "คุณกำลังเรียกใช้ runme.sh" ได้อย่างไรโดยไม่ต้องใช้ hardcoding


3
ที่คล้ายกัน [สคริปต์ทุบตีสามารถบอกสิ่งที่ไดเรกทอรีมันเก็บไว้ใน?] (เพื่อstackoverflow.com/questions/59895/ … )
Rodriguez

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

คำตอบ:


630
me=`basename "$0"`

สำหรับการอ่านผ่าน symlink 1ซึ่งโดยปกติจะไม่ใช่สิ่งที่คุณต้องการ (โดยปกติคุณไม่ต้องการให้ผู้ใช้สับสนด้วยวิธีนี้) ให้ลอง:

me="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")"

IMO ซึ่งจะสร้างผลลัพธ์ที่สับสน "ฉันวิ่ง foo.sh, แต่มันบอกว่าฉันกำลังใช้ bar.sh!? ต้องเป็นบั๊ก!" นอกจากนี้หนึ่งในวัตถุประสงค์ของการมี symlink ที่มีชื่อต่างกันคือการจัดเตรียมการทำงานที่แตกต่างกันตามชื่อที่เรียกว่า (คิดว่า gzip และ gunzip ในบางแพลตฟอร์ม)


1นั่นคือการ symlinks แก้ปัญหาดังกล่าวว่าเมื่อผู้ใช้รันfoo.shซึ่งเป็นจริง symlink ไปbar.shคุณต้องการที่จะใช้ชื่อแก้ไขมากกว่าbar.shfoo.sh


40
$ 0 ให้ชื่อซึ่งสคริปต์ถูกเรียกใช้ไม่ใช่เส้นทางที่แท้จริงของไฟล์สคริปต์จริง
Chris Conway

4
ใช้งานได้เว้นแต่คุณจะถูกเรียกผ่าน symlink แต่ถึงอย่างนั้นมันก็เป็นเรื่องปกติที่คุณต้องการ IME
Tanktalus

78
ไม่ทำงานสำหรับสคริปต์ที่มีที่มาซึ่งต่างไปจากที่เรียกใช้
ชาร์ลส์ดัฟฟี่

1
@Charles ดัฟฟี่: ดูคำตอบ Dimitre Radoulov ด้านล่าง
Serge Wautier

8
-1, 1. readlink จะเดินทางเพียงหนึ่ง symlink ที่ลึก 2. $0ในตัวอย่างแรกนั้นขึ้นอยู่กับการแบ่งคำ 3. $0จะถูกส่งผ่านไปยัง basename, readlink และ echo ในตำแหน่งที่อนุญาตให้ถือว่าเป็นสวิตช์บรรทัดคำสั่ง . ฉันแนะนำแทนme=$(basename -- "$0")หรืออย่างมีประสิทธิภาพมากกว่าที่ค่าใช้จ่ายในการอ่าน, me=${0##*/}. สำหรับ symlink me=$(basename -- "$(readlink -f -- "$0")")สมมติว่า gnu utils มิฉะนั้นจะเป็นสคริปต์ที่ยาวมากซึ่งฉันจะไม่เขียนที่นี่
คะแนน _ ต่ำกว่า

246
# ------------- SCRIPT ------------- # # ------------- CALLED ------ ------- #

#!/bin/bash

echo
echo "# arguments called with ---->  ${@}     "
echo "# \$1 ---------------------->  $1       "
echo "# \$2 ---------------------->  $2       "
echo "# path to me --------------->  ${0}     "
echo "# parent path -------------->  ${0%/*}  "
echo "# my name ------------------>  ${0##*/} "
echo
exit



# ประกาศในบรรทัดถัดไปอาร์กิวเมนต์แรกถูกเรียกใช้ภายใน double, # และเครื่องหมายอัญประกาศเดี่ยวเนื่องจากมีสองคำ


$   / เบ็ดเตล็ด/ shell_scripts / check_root / show_parms sh "'hello there'" "'william'" 

# ------------- ผล ------------- #

# อาร์กิวเมนต์ที่เรียกด้วย ---> 'สวัสดีนั่น' 'วิลเลียม' # $ 1 ----------------------> 'สวัสดีที่นั่น' # $ 2 ---- ------------------> เส้นทาง'วิลเลียม' # มาหาฉัน --------------> / misc / shell_scripts / check_root / show_parms sh # พา ธ หลัก -------------> / misc / shell_scripts / check_root # ชื่อของฉัน -----------------> show_parms.sh






# ------------- END ------------- #

11
@NickC ดูการกำจัด
cychoi

1
ฉันจะได้รับเพียงแค่show_paramsชื่อคือไม่มีนามสกุลไม่จำเป็น?
คิวเมนทัส

ไม่ทำงานในกรณีที่สคริปต์ถูกเรียกใช้จากโฟลเดอร์อื่น ${0##*/}เส้นทางดังกล่าวจะรวมอยู่ใน ผ่านการทดสอบโดยใช้ GitBash
AlikElzin-kilaka

1
ข้างต้นไม่ได้ผลสำหรับฉันจากสคริปต์. bash_login แต่โซลูชัน Dimitre Radoulov ของ $ BASH_SOURCE ใช้งานได้ดี
จอห์น

@ John แน่นอนคุณหมายถึง.bash_profile ? หรือมิฉะนั้น.bashrc ? คุณควรรู้ว่ามีความแตกต่างเล็กน้อยในวิธีการที่พวกเขาเรียกใช้ / ที่มา ฉันเหนื่อยมาก แต่ถ้าฉันคิดถูกมันคงเป็นปัญหา ที่เดียวที่สำคัญคือถ้าคุณลงชื่อเข้าใช้ระบบจากระยะไกลเช่น ssh กับที่ระบบโลคอล
Pryftan

193

ด้วยbash> = 3งานต่อไปนี้:

$ ./s
0 is: ./s
BASH_SOURCE is: ./s
$ . ./s
0 is: bash
BASH_SOURCE is: ./s

$ cat s
#!/bin/bash

printf '$0 is: %s\n$BASH_SOURCE is: %s\n' "$0" "$BASH_SOURCE"

24
ที่ดี! นั่นเป็นคำตอบที่ใช้ได้ทั้ง. /scrip.sh และ source ./script.sh
zhaorufei

4
นี่คือสิ่งที่ฉันต้องการและใช้ง่าย " dirname $BASE_SOURCE" เพื่อรับไดเรกทอรีที่ตั้งสคริปต์
Larry Cai

2
ฉันเกือบจะได้เรียนรู้ว่าวิธีที่แตกต่างนั้นยากเมื่อเขียนสคริปต์การลบตัวเอง โชคดีที่ 'rm' ถูกใช้เป็น 'rm -i' :)
kervin

ต่อไป ./s เพื่อรับชื่อ. / แทนที่จะเป็นทุบตี? ฉันพบว่า $ 1 ไม่ได้ถูกตั้งค่าเป็น. / ... เสมอไป ...
David Mokon Bond

1
มันอยู่ในBASH_SOURCEใช่ไหม
Dimitre Radoulov

69

$BASH_SOURCE ให้คำตอบที่ถูกต้องเมื่อทำการจัดหาสคริปต์

ซึ่งรวมถึงเส้นทางเพื่อให้ได้รับชื่อไฟล์สคริปต์เท่านั้นใช้:

$(basename $BASH_SOURCE) 

2
คำตอบนี้เป็น IMHO ที่ดีที่สุดเพราะวิธีการแก้ปัญหาทำให้ใช้รหัสเอกสารด้วยตนเอง $ BASH_SOURCE สามารถเข้าใจได้อย่างสมบูรณ์โดยไม่ต้องอ่านเอกสารใด ๆ ในขณะที่ $ {0 ## * /} ไม่ได้
Andreas M. Oberheim

คำตอบนี้มีค่ามากขึ้นผมเชื่อเพราะถ้าเราทำงานเหมือน. <filename> [arguments], $0จะให้ชื่อของเปลือกโทร อย่างน้อยใน OSX อย่างแน่นอน
Mihir

68

หากชื่อสคริปต์ที่มีช่องว่างในมันเป็นวิธีที่มีประสิทธิภาพมากขึ้นคือการใช้"$0"หรือ"$(basename "$0")"- หรือบน "$(basename \"$0\")"MacOS: สิ่งนี้จะป้องกันไม่ให้ชื่อถูกจัดการหรือตีความในทางใดทางหนึ่ง โดยทั่วไปแล้วมันเป็นการดีที่จะใช้ชื่อตัวแปรอัญประกาศคู่เสมอในเชลล์


2
+1 สำหรับคำตอบโดยตรง + รัดกุม หากต้องการคุณสมบัติ symlink ฉันขอแนะนำให้ดู: คำตอบของ Travis B. Hartwell
เทรเวอร์บอยด์สมิ ธ

26

หากคุณต้องการโดยไม่มีเส้นทางคุณจะใช้ ${0##*/}


และถ้าฉันต้องการมันโดยไม่มีนามสกุลไฟล์เสริม?
คิวเมนตัส

หากต้องการลบส่วนขยายคุณสามารถลอง "$ {VARIABLE% .ext}" โดยที่ VARIABLE คือค่าที่คุณได้รับจาก $ {0 ## * /} และ ".ext" เป็นส่วนขยายที่คุณต้องการลบ
BrianV

19

หากต้องการตอบChris Conwayบน Linux (อย่างน้อย) คุณต้องทำสิ่งนี้:

echo $(basename $(readlink -nf $0))

readlink พิมพ์ค่าของลิงก์สัญลักษณ์ หากไม่ใช่ลิงค์สัญลักษณ์จะพิมพ์ชื่อไฟล์ -n บอกให้ไม่พิมพ์บรรทัดใหม่ - f บอกให้ติดตามลิงก์อย่างสมบูรณ์ (หากลิงก์สัญลักษณ์เป็นลิงก์ไปยังลิงก์อื่นก็จะแก้ไขได้เช่นกัน)


2
-n ไม่เป็นอันตราย แต่ไม่จำเป็นเพราะโครงสร้าง $ (... ) จะตัดมัน
zhaorufei

16

ฉันพบว่าบรรทัดนี้ใช้งานได้เสมอโดยไม่คำนึงว่าไฟล์นั้นมีแหล่งที่มาหรือเรียกใช้เป็นสคริปต์

echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"

หากคุณต้องการติดตามการใช้ symlink readlinkบนเส้นทางที่คุณได้รับด้านบนเรียกซ้ำหรือไม่เรียกซ้ำ

เหตุผลหนึ่งซับการทำงานจะมีการอธิบายจากการใช้งานของBASH_SOURCEตัวแปรสภาพแวดล้อมและ FUNCNAMEบริษัท ร่วม

BASH_SOURCE

ตัวแปรอาร์เรย์ที่สมาชิกเป็นชื่อไฟล์ต้นทางที่ชื่อฟังก์ชันเชลล์ที่สอดคล้องกันในตัวแปรอาร์เรย์ FUNCNAME ถูกกำหนดไว้ ฟังก์ชันเชลล์ $ {FUNCNAME [$ i]} ถูกกำหนดไว้ในไฟล์ $ {BASH_SOURCE [$ i]} และเรียกใช้จาก $ {BASH_SOURCE [$ i + 1]}

FUNCNAME

ตัวแปรอาร์เรย์ที่มีชื่อของฟังก์ชั่นเชลล์ทั้งหมดในปัจจุบันในการเรียกการเรียกใช้งาน องค์ประกอบที่มีดัชนี 0 เป็นชื่อของฟังก์ชั่นเปลือกที่กำลังดำเนินการใด ๆ ในปัจจุบัน องค์ประกอบด้านล่างสุด (องค์ประกอบที่มีดัชนีสูงสุด) คือ "หลัก" ตัวแปรนี้มีอยู่เฉพาะเมื่อมีการเรียกใช้ฟังก์ชันเชลล์ การกำหนดให้กับ FUNCNAME ไม่มีผลกระทบและส่งคืนสถานะข้อผิดพลาด หาก FUNCNAME ไม่ได้ตั้งค่ามันจะสูญเสียคุณสมบัติพิเศษแม้ว่าจะถูกรีเซ็ตในภายหลัง

ตัวแปรนี้สามารถใช้กับ BASH_LINENO และ BASH_SOURCE แต่ละองค์ประกอบของ FUNCNAME มีองค์ประกอบที่สอดคล้องกันใน BASH_LINENO และ BASH_SOURCE เพื่ออธิบายสายการโทร ตัวอย่างเช่น $ {FUNCNAME [$ i]} ถูกเรียกจากไฟล์ $ {BASH_SOURCE [$ i + 1]} ที่หมายเลขบรรทัด $ {BASH_LINENO [$ i]} ตัวเรียกในตัวแสดงสแต็กการโทรปัจจุบันโดยใช้ข้อมูลนี้

[ที่มา: คู่มือทุบตี]


2
มันทำงานได้ถ้าคุณแหล่งที่มาในไฟล์a (ถือว่าaเนื้อหาเป็นecho "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}") จากเซสชั่นแบบโต้ตอบ - แล้วมันจะให้aเส้นทางของคุณ แต่ถ้าคุณเขียนสคริปต์bที่มีsource aอยู่ในนั้นและเรียกใช้./bก็จะกลับมาb's เส้นทาง
PSkocik

12

คำตอบเหล่านี้ถูกต้องสำหรับกรณีที่พวกเขาระบุ แต่ยังคงมีปัญหาหากคุณเรียกใช้สคริปต์จากสคริปต์อื่นโดยใช้คำหลัก 'แหล่งที่มา' (เพื่อให้มันทำงานในเชลล์เดียวกัน) ในกรณีนี้คุณจะได้รับ $ 0 ของสคริปต์การโทร และในกรณีนี้ฉันไม่คิดว่าเป็นไปได้ที่จะได้ชื่อของสคริปต์นั้น

นี่เป็นกรณีที่ไม่ควรนำมาใช้อย่างจริงจัง หากคุณเรียกใช้สคริปต์จากสคริปต์อื่นโดยตรง (โดยไม่มี 'แหล่งที่มา') การใช้ $ 0 จะใช้งานได้


2
คุณมีจุดที่ดีมาก ไม่ใช่กรณีขอบ IMO มีวิธีแก้ปัญหาคือ: ดูคำตอบของ Dimitre Radoulov ด้านบน
Serge Wautier

10

เนื่องจากความคิดเห็นบางส่วนถามเกี่ยวกับชื่อไฟล์ที่ไม่มีนามสกุลนี่คือตัวอย่างวิธีการทำให้สำเร็จ:

FileName=${0##*/}
FileNameWithoutExtension=${FileName%.*}

สนุก!


9

Re: คำตอบของ Tanktalus (เป็นที่ยอมรับ) ข้างต้นวิธีใช้ที่สะอาดกว่านี้เล็กน้อย:

me=$(readlink --canonicalize --no-newline $0)

หากสคริปต์ของคุณได้มาจากสคริปต์ทุบตีอื่นคุณสามารถใช้:

me=$(readlink --canonicalize --no-newline $BASH_SOURCE)

ฉันยอมรับว่ามันอาจสร้างความสับสนในการยกเลิกการเชื่อมโยงหากวัตถุประสงค์ของคุณคือการให้ข้อเสนอแนะกับผู้ใช้ แต่มีหลายครั้งที่คุณจำเป็นต้องได้รับชื่อมาตรฐานจากสคริปต์หรือไฟล์อื่น ๆ และนี่คือวิธีที่ดีที่สุด


1
ขอบคุณสำหรับsourceชื่อสคริปต์
Fredrick Gauss

8

คุณสามารถใช้ $ 0 เพื่อกำหนดชื่อสคริปต์ของคุณ (พร้อมเส้นทางแบบเต็ม) - เพื่อให้ได้ชื่อสคริปต์เท่านั้นคุณสามารถตัดตัวแปรนั้นด้วย

basename $0

8

ถ้าคุณเรียกใช้สคริปต์เปลือกเช่น

/home/mike/runme.sh

$ 0 เป็นชื่อเต็ม

 /home/mike/runme.sh

basename $ 0 จะได้รับชื่อไฟล์ฐาน

 runme.sh

และคุณต้องใส่ชื่อพื้นฐานนี้ลงในตัวแปรเช่น

filename=$(basename $0)

และเพิ่มข้อความเพิ่มเติมของคุณ

echo "You are running $filename"

ดังนั้นสคริปต์ของคุณชอบ

/home/mike/runme.sh
#!/bin/bash 
filename=$(basename $0)
echo "You are running $filename"

7
this="$(dirname "$(realpath "$BASH_SOURCE")")"

สิ่งนี้จะแก้ไขลิงก์สัญลักษณ์ (realpath ทำเช่นนั้น) จัดการช่องว่าง (อัญประกาศทำสิ่งนี้) และจะค้นหาชื่อสคริปต์ปัจจุบันแม้ว่าจะมีแหล่งที่มา (./myscript) หรือถูกเรียกโดยสคริปต์อื่น ($ BASH_SOURCE จะจัดการกับสิ่งนั้น) หลังจากนั้นก็เป็นการดีที่จะบันทึกสิ่งนี้ไว้ในตัวแปรสภาพแวดล้อมเพื่อนำกลับมาใช้ใหม่หรือคัดลอกได้ง่ายที่อื่น (this =) ...


3
FYI realpathไม่ใช่คำสั่ง BASH ในตัว มันเป็นปฏิบัติการแบบสแตนด์อโลนที่มีเฉพาะในการแจกแจงบางอย่าง
StvnW

4

ในคุณจะได้รับชื่อไฟล์สคริปต์ที่ใช้bash $0โดยทั่วไป$1แล้ว$2การเข้าถึงข้อโต้แย้งของ CLI ในทำนองเดียวกัน$0คือการเข้าถึงชื่อที่เรียกสคริปต์ (ชื่อไฟล์สคริปต์)

#!/bin/bash
echo "You are running $0"
...
...

หากคุณเรียกใช้สคริปต์ด้วยเส้นทางเช่น/path/to/script.shนั้น$0ก็จะให้ชื่อไฟล์ที่มีเส้นทาง ในกรณีนั้นจำเป็นต้องใช้$(basename $0)เพื่อรับชื่อไฟล์สคริปต์เท่านั้น



2

$0ไม่ตอบคำถาม (ตามที่ฉันเข้าใจ) การสาธิต:

$ cat script.sh
#! bin / sh /
echo `basename $ 0`
$ ./script.sh 
script.sh
$ ln script.sh linktoscript
$ ./linktoscript 
linktoscript

เรา./linktoscriptจะพิมพ์ออกมาได้script.shอย่างไร

[แก้ไข] ต่อ @ephemient ในความคิดเห็นด้านบนถึงแม้ว่าสิ่งที่เชื่อมโยงสัญลักษณ์อาจดูเหมือนว่าถูกประดิษฐ์ขึ้น แต่ก็เป็นไปได้ที่จะทำ$0เช่นนี้โดยไม่ได้แสดงถึงทรัพยากรระบบแฟ้ม OP ค่อนข้างคลุมเครือเกี่ยวกับสิ่งที่เขาต้องการ


เพิ่มคำตอบนี้ไปคำตอบของฉันข้างต้น แต่ผมไม่เห็นว่านี่คือสิ่งที่ต้องการจริงๆ (หรือว่าคุณควรจะต้องการยกเว้นเหตุขัดข้องบางอย่างที่ผมไม่สามารถเข้าใจในปัจจุบัน)
Tanktalus

2
ฉันคิดว่าการพิมพ์ linktoscript แทน script.sh เป็นคุณสมบัติไม่ใช่ข้อผิดพลาด คำสั่ง unix หลายคำใช้ชื่อเพื่อเปลี่ยนพฤติกรรมของพวกเขา ตัวอย่างคือ vi / ex / view
mouviciel

@ ถังทาลัส: มีหลายกรณีที่คุณต้องการให้พฤติกรรมเปลี่ยนไปตามตำแหน่งที่แท้จริงของไฟล์ - ในโครงการก่อนหน้านี้ฉันมีสคริปต์ "สาขาที่ใช้งานอยู่" ซึ่งจะเชื่อมโยงไปยังเส้นทางเครื่องมือหลายอย่างจากสาขาที่ฉันเป็น ทำงานบน; เครื่องมือเหล่านั้นสามารถค้นหาไดเรกทอรีที่พวกเขาต้องการตรวจสอบเพื่อทำงานกับไฟล์ที่ถูกต้อง
Andrew Aylett

2

ข้อมูลขอบคุณ Bill Hernandez ฉันได้เพิ่มการตั้งค่าบางอย่างที่ฉันยอมรับ

#!/bin/bash
function Usage(){
    echo " Usage: show_parameters [ arg1 ][ arg2 ]"
}
[[ ${#2} -eq 0 ]] && Usage || {
    echo
    echo "# arguments called with ---->  ${@}     "
    echo "# \$1 ----------------------->  $1       "
    echo "# \$2 ----------------------->  $2       "
    echo "# path to me --------------->  ${0}     " | sed "s/$USER/\$USER/g"
    echo "# parent path -------------->  ${0%/*}  " | sed "s/$USER/\$USER/g"
    echo "# my name ------------------>  ${0##*/} "
    echo
}

ไชโย


1

สั้นชัดเจนและเรียบง่ายมา my_script.sh

#!/bin/bash

running_file_name=$(basename "$0")

echo "You are running '$running_file_name' file."

ออกวาง:

./my_script.sh
You are running 'my_script.sh' file.


0

นี่คือสิ่งที่ฉันมาด้วยแรงบันดาลใจจากDimitre Radoulovคำตอบ 's (ซึ่งผม upvoted โดยวิธีการ)

script="$BASH_SOURCE"
[ -z "$BASH_SOURCE" ] && script="$0"

echo "Called $script with $# argument(s)"

ไม่ว่าคุณจะเรียกสคริปต์ของคุณอย่างไร

. path/to/script.sh

หรือ

./path/to/script.sh


-6

อะไรแบบนี้?

export LC_ALL=en_US.UTF-8
#!/bin/bash
#!/bin/sh

#----------------------------------------------------------------------
start_trash(){
ver="htrash.sh v0.0.4"
$TRASH_DIR  # url to trash $MY_USER
$TRASH_SIZE # Show Trash Folder Size

echo "Would you like to empty Trash  [y/n]?"
read ans
if [ $ans = y -o $ans = Y -o $ans = yes -o $ans = Yes -o $ans = YES ]
then
echo "'yes'"
cd $TRASH_DIR && $EMPTY_TRASH
fi
if [ $ans = n -o $ans = N -o $ans = no -o $ans = No -o $ans = NO ]
then
echo "'no'"
fi
 return $TRUE
} 
#-----------------------------------------------------------------------

start_help(){
echo "HELP COMMANDS-----------------------------"
echo "htest www                 open a homepage "
echo "htest trash               empty trash     "
 return $TRUE
} #end Help
#-----------------------------------------------#

homepage=""

return $TRUE
} #end cpdebtemp

# -Case start
# if no command line arg given
# set val to Unknown
if [ -z $1 ]
then
  val="*** Unknown  ***"
elif [ -n $1 ]
then
# otherwise make first arg as val
  val=$1
fi
# use case statement to make decision for rental
case $val in
   "trash") start_trash ;;
   "help") start_help ;;
   "www") firefox $homepage ;;
   *) echo "Sorry, I can not get a $val   for you!";;
esac
# Case stop

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