Bagaimana saya tahu nama file skrip dalam skrip Bash?

606

Bagaimana saya bisa menentukan nama file skrip Bash di dalam skrip itu sendiri?

Seperti jika skrip saya ada dalam file runme.sh, lalu bagaimana saya membuatnya untuk menampilkan pesan "Anda menjalankan runme.sh" tanpa hardcoding itu?

Ma99uS
sumber
3
Mirip [Bisakah skrip bash mengetahui direktori tempat penyimpanannya?] ( To stackoverflow.com/questions/59895/… )
Rodrigue
Untuk direktori, lihat: Mendapatkan direktori sumber skrip Bash dari dalam .
kenorb

Jawaban:

631
me=`basename "$0"`

Untuk membaca symlink 1 , yang biasanya bukan yang Anda inginkan (Anda biasanya tidak ingin membingungkan pengguna dengan cara ini), cobalah:

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

IMO, itu akan menghasilkan output yang membingungkan. "Aku berlari foo.sh, tapi katanya aku menjalankan bar.sh !? Pasti bug!" Selain itu, salah satu tujuan memiliki symlink yang berbeda nama adalah untuk menyediakan fungsionalitas yang berbeda berdasarkan nama yang disebutnya (pikirkan gzip dan gunzip pada beberapa platform).


1 Yaitu, untuk menyelesaikan symlink sehingga ketika pengguna mengeksekusi foo.shyang sebenarnya merupakan symlink bar.sh, Anda ingin menggunakan nama yang diselesaikan bar.shalih-alih foo.sh.

Tanktalus
sumber
40
$ 0 memberi Anda nama melalui mana skrip dipanggil, bukan path sebenarnya dari file skrip yang sebenarnya.
Chris Conway
4
Ini berfungsi kecuali Anda dipanggil melalui symlink. Tapi, meski begitu, biasanya itu yang Anda inginkan, IME.
Tanktalus
78
Tidak berfungsi untuk skrip yang bersumber sebagai lawan dipanggil.
Charles Duffy
1
@Charles Duffy: Lihat jawaban Dimitre Radoulov di bawah ini .
Serge Wautier
8
-1, 1. readlink hanya akan berjalan dalam satu symlink, 2. $0pada contoh pertama tunduk pada pemisahan kata, 3. $0diteruskan ke nama file, readlink, dan gema dalam posisi yang memungkinkannya untuk diperlakukan sebagai saklar baris perintah . Saya menyarankan sebagai gantinya me=$(basename -- "$0")atau jauh lebih efisien dengan mengorbankan keterbacaan me=${0##*/},. Untuk symlink, me=$(basename -- "$(readlink -f -- "$0")")dengan asumsi gnu utils, jika tidak maka akan menjadi skrip yang sangat panjang yang tidak akan saya tulis di sini.
Score_Under
246
# ------------- SCRIPT ------------- # # ------------- DISEBUT ------ ------- #

#!/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



# Perhatikan pada baris berikutnya, argumen pertama disebut dalam tanda kutip ganda, # dan tunggal, karena mengandung dua kata


$   / misc / shell_scripts / check_root / show_parms . sh "'halo di sana'" "'william'" 

# ------------- HASIL ------------- #

# argumen dipanggil dengan ---> 'hello there' 'william' # $ 1 ----------------------> 'hello there' # $ 2 ---- ------------------> path 'william' ke saya --------------> / misc / shell_scripts / check_root / show_parms. sh # parent path -------------> / misc / shell_scripts / check_root # nama saya -----------------> show_parms.sh






# ------------- AKHIR ------------- #
Bill Hernandez
sumber
11
@NickC lihat Substring Removal
cychoi
1
Bagaimana cara saya mendapatkan adil show_params, yaitu nama tanpa ekstensi opsional?
Acumenus
Tidak berfungsi jika skrip dipanggil dari folder lain. Path disertakan dalam ${0##*/}. Diuji menggunakan GitBash.
AlikElzin-kilaka
1
Di atas tidak bekerja untuk saya dari skrip .bash_login, tetapi solusi Dimitre Radoulov dari $ BASH_SOURCE berfungsi dengan baik.
Yohanes
@ John Tentunya maksud Anda .ash_profile ? Atau sebagai alternatif .bashrc ? Anda harus tahu bahwa ada beberapa perbedaan halus dalam bagaimana mereka dipanggil / bersumber. Saya sangat lelah tetapi jika saya berpikir benar itu kemungkinan besar masalahnya. Satu tempat yang penting adalah jika Anda masuk ke sistem dari jarak jauh misalnya ssh versus di sistem lokal.
Pryftan
193

Dengan bash> = 3 , karya-karya berikut:

$ ./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"
Dimitre Radoulov
sumber
24
Bagus! Itulah jawaban yang bekerja untuk ./script.sh dan sumber ./script.sh
zhaorufei
4
Ini yang saya inginkan, dan mudah menggunakan " dirname $BASE_SOURCE" untuk mendapatkan direktori tempat skrip berada.
Larry Cai
2
Saya hampir mempelajari perbedaan itu dengan cara yang sulit ketika menulis naskah penghapus sendiri. Untungnya 'rm' alias 'rm -i' :)
kervin
toh untuk. ./s untuk mendapatkan nama ./s bukannya bash? Saya telah menemukan bahwa $ 1 tidak selalu ditetapkan untuk ./s ...
David Mokon Bond
1
Ada di BASH_SOURCE , bukan?
Dimitre Radoulov
69

$BASH_SOURCE memberikan jawaban yang benar saat sumber skrip.

Namun ini termasuk path sehingga untuk mendapatkan nama file skrip saja, gunakan:

$(basename $BASH_SOURCE) 
Zainka
sumber
2
Jawaban ini adalah IMHO yang terbaik karena solusi ini menggunakan kode self-documenting. $ BASH_SOURCE benar-benar dapat dimengerti tanpa membaca dokumentasi apa pun sedangkan misalkan $ {0 ## * /} tidak
Andreas M. Oberheim
Jawaban ini adalah nilai lebih yang saya yakini karena jika dijalankan seperti . <filename> [arguments], $0akan memberikan nama penelepon shell. Yah setidaknya di OSX pasti.
Mihir
68

Jika nama script memiliki ruang di dalamnya, cara yang lebih kuat adalah dengan menggunakan "$0"atau "$(basename "$0")"- atau MacOS: "$(basename \"$0\")". Ini mencegah nama menjadi rusak atau ditafsirkan dengan cara apa pun. Secara umum, itu adalah praktik yang baik untuk selalu menggandakan nama variabel dalam shell.

Josh Lee
sumber
2
+1 untuk jawaban langsung + ringkas. jika perlu fitur symlink saya sarankan melihat: jawaban Travis B. Hartwell.
Trevor Boyd Smith
26

Jika Anda menginginkannya tanpa path maka Anda akan menggunakannya ${0##*/}

Tuan Muskrat
sumber
Dan bagaimana jika saya menginginkannya tanpa ekstensi file opsional?
Acumenus
Untuk menghapus ekstensi, Anda dapat mencoba "$ {VARIABLE% .ext}" di mana VARIABLE adalah nilai yang Anda dapatkan dari $ {0 ## * /} dan ".ext" adalah ekstensi yang ingin Anda hapus.
BrianV
19

Untuk menjawab Chris Conway , di Linux (setidaknya) Anda akan melakukan ini:

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

readlink mencetak nilai tautan simbolik. Jika itu bukan tautan simbolis, ia mencetak nama file. -n mengatakan itu untuk tidak mencetak baris baru. -f mengatakan itu untuk mengikuti tautan sepenuhnya (jika tautan simbolis adalah tautan ke tautan lain, itu akan menyelesaikannya juga).

Travis B. Hartwell
sumber
2
-n tidak berbahaya tetapi tidak diperlukan karena struktur $ (...) akan memotongnya.
zhaorufei
16

Saya menemukan baris ini selalu berfungsi, terlepas dari apakah file tersebut bersumber atau dijalankan sebagai skrip.

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

Jika Anda ingin mengikuti symlink, gunakan readlinkjalur yang Anda dapatkan di atas, secara rekursif atau non-rekursif.

Alasan one-liner berfungsi dijelaskan oleh penggunaan BASH_SOURCEvariabel lingkungan dan asosiasinya FUNCNAME.

BASH_SOURCE

Variabel array yang anggotanya adalah nama file sumber tempat nama fungsi shell yang sesuai dalam variabel array FUNCNAME didefinisikan. Fungsi shell $ {FUNCNAME [$ i]} didefinisikan dalam file $ {BASH_SOURCE [$ i]} dan dipanggil dari $ {BASH_SOURCE [$ i + 1]}.

FUNCNAME

Variabel array yang berisi nama-nama semua fungsi shell saat ini dalam tumpukan panggilan eksekusi. Elemen dengan indeks 0 adalah nama dari setiap fungsi shell yang sedang dijalankan. Elemen terbawah (yang memiliki indeks tertinggi) adalah "utama". Variabel ini hanya ada ketika fungsi shell mengeksekusi. Tugas untuk FUNCNAME tidak berpengaruh dan mengembalikan status kesalahan. Jika FUNCNAME tidak disetel, FUNCNAME akan kehilangan properti khususnya, bahkan jika kemudian disetel ulang.

Variabel ini dapat digunakan dengan BASH_LINENO dan BASH_SOURCE. Setiap elemen FUNCNAME memiliki elemen yang sesuai di BASH_LINENO dan BASH_SOURCE untuk menjelaskan tumpukan panggilan. Misalnya, $ {FUNCNAME [$ i]} dipanggil dari file $ {BASH_SOURCE [$ i + 1]} di nomor baris $ {BASH_LINENO [$ i]}. Penelepon bawaan menampilkan tumpukan panggilan saat ini menggunakan informasi ini.

[Sumber: Bash manual]

gkb0986
sumber
2
Ini berfungsi jika Anda sumber dalam file a (anggap aisinya echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}") dari sesi interaktif - maka itu akan memberi Anda ajalan. Tetapi jika Anda menulis naskah bdengan source adi dalamnya dan menjalankannya ./b, itu akan kembali bjalur 's.
PSkocik
12

Jawaban ini benar untuk kasus yang mereka nyatakan tetapi masih ada masalah jika Anda menjalankan skrip dari skrip lain menggunakan kata kunci 'sumber' (sehingga berjalan di shell yang sama). Dalam hal ini, Anda mendapatkan $ 0 dari skrip panggilan. Dan dalam hal ini, saya rasa tidak mungkin untuk mendapatkan nama skrip itu sendiri.

Ini adalah kasus tepi dan tidak boleh dianggap terlalu serius. Jika Anda menjalankan skrip dari skrip lain secara langsung (tanpa 'sumber'), menggunakan $ 0 akan berfungsi.

Jim Dodd
sumber
2
Anda memiliki poin yang sangat bagus. Bukan IMO kasus tepi. Namun ada solusinya: Lihat jawaban Dimitre Radoulov di atas
Serge Wautier
10

Karena beberapa komentar bertanya tentang nama file tanpa ekstensi, berikut ini adalah contoh bagaimana melakukannya:

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

Nikmati!

Simon Mattes
sumber
9

Re: Jawaban Tanktalus (diterima) di atas, cara yang sedikit lebih bersih adalah dengan menggunakan:

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

Jika skrip Anda bersumber dari skrip bash lain, Anda dapat menggunakan:

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

Saya setuju bahwa itu akan membingungkan untuk symlink dereference jika tujuan Anda adalah untuk memberikan umpan balik kepada pengguna, tetapi ada saat-saat ketika Anda perlu mendapatkan nama kanonik ke skrip atau file lain, dan ini adalah cara terbaik, imo.

simon
sumber
1
Terima kasih untuk sourcenama skrip d.
Fredrick Gauss
8

Anda dapat menggunakan $ 0 untuk menentukan nama skrip Anda (dengan path lengkap) - untuk mendapatkan nama skrip saja Anda dapat memangkas variabel itu

basename $0
VolkA
sumber
8

jika skrip shell Anda aktifkan

/home/mike/runme.sh

$ 0 adalah nama lengkap

 /home/mike/runme.sh

basename $ 0 akan mendapatkan nama file dasar

 runme.sh

dan Anda perlu memasukkan nama dasar ini ke dalam variabel seperti

filename=$(basename $0)

dan tambahkan teks tambahan Anda

echo "You are running $filename"

jadi skrip Anda suka

/home/mike/runme.sh
#!/bin/bash 
filename=$(basename $0)
echo "You are running $filename"
LawrenceLi
sumber
7
this="$(dirname "$(realpath "$BASH_SOURCE")")"

Ini menyelesaikan tautan simbolik (realpath melakukan itu), menangani spasi (tanda kutip ganda melakukan ini), dan akan menemukan nama skrip saat ini bahkan ketika bersumber (./myscript) atau dipanggil oleh skrip lain ($ BASH_SOURCE menangani itu). Setelah semua itu, ada baiknya menyimpan ini dalam variabel lingkungan untuk digunakan kembali atau untuk salinan mudah di tempat lain (ini =) ...

jcalfee314
sumber
3
FYI realpathbukan perintah BASH bawaan. Ini adalah executable mandiri yang hanya tersedia di distribusi tertentu
StvnW
4

Di bashAnda bisa mendapatkan nama file skrip menggunakan $0. Biasanya $1, $2dll untuk mengakses argumen CLI. Demikian pula $0untuk mengakses nama yang memicu skrip (nama file skrip).

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

Jika Anda memanggil skrip dengan path seperti /path/to/script.shmaka $0juga akan memberikan nama file dengan path. Dalam hal ini perlu digunakan $(basename $0)untuk mendapatkan hanya nama file skrip.

rashok
sumber
3
echo "$(basename "`test -L ${BASH_SOURCE[0]} \
                   && readlink ${BASH_SOURCE[0]} \
                   || echo ${BASH_SOURCE[0]}`")"
ecwpz91
sumber
2

$0tidak menjawab pertanyaan (seperti yang saya mengerti). Demonstrasi:

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

Bagaimana cara ./linktoscriptmencetak script.sh?

[EDIT] Per @ephemient dalam komentar di atas, meskipun hal tautan simbolis tampaknya dibuat-buat, dimungkinkan untuk mengutak-atik $0sedemikian rupa sehingga tidak mewakili sumber daya sistem file. OP agak ambigu tentang apa yang diinginkannya.

Chris Conway
sumber
menambahkan jawaban untuk ini untuk jawaban saya di atas, tetapi saya tidak setuju bahwa ini adalah apa yang benar-benar diinginkan (atau bahwa Anda harus menginginkannya, kecuali beberapa keadaan khusus yang saat ini tidak dapat saya pahami)
Tanktalus
2
Saya pikir mencetak linktoscript bukan script.sh adalah fitur, bukan bug. Beberapa perintah unix menggunakan namanya untuk mengubah perilaku mereka. Contohnya adalah vi / ex / view.
mouviciel
@Tanktalus: Ada kasus di mana Anda ingin perilaku berubah berdasarkan lokasi sebenarnya file - pada proyek sebelumnya saya memiliki skrip "cabang aktif", yang akan terhubung ke jalan beberapa alat dari cabang saya bekerja pada; alat-alat itu kemudian bisa mencari tahu direktori mana mereka telah diperiksa untuk menjalankan terhadap file yang tepat.
Andrew Aylett
2

Info terima kasih kepada Bill Hernandez. Saya menambahkan beberapa preferensi yang saya adopsi.

#!/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
}

Bersulang

linxuser
sumber
1

Singkat, jelas dan sederhana, dalam my_script.sh

#!/bin/bash

running_file_name=$(basename "$0")

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

Keluarkan:

./my_script.sh
You are running 'my_script.sh' file.
Bơ Loong A Nhứi
sumber
0

Inilah yang saya kemukakan , diilhami oleh jawaban Dimitre Radoulov (yang saya sampaikan, dengan cara) .

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

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

terlepas dari cara Anda memanggil skrip Anda

. path/to/script.sh

atau

./path/to/script.sh
Salathiel Genèse
sumber
-1

echo "Anda menjalankan $ 0"

mmacaulay
sumber
Bukan skrip bash, ini path lengkap.
e-info128
-6

sesuatu seperti ini?

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
hynt
sumber
5
-1, tidak menjawab pertanyaan (tidak menunjukkan cara menemukan nama skrip), dan merupakan contoh yang membingungkan dalam skrip yang sangat bermasalah.
Score_Under