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?


3
Mirip [Bisakah skrip bash mengetahui direktori tempat penyimpanannya?] ( To stackoverflow.com/questions/59895/… )
Rodrigue

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.


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


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

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"

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) 

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.


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


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).


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]


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.


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!


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.


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

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"

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 =) ...


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.


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

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.


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


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.


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


-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

5
-1, tidak menjawab pertanyaan (tidak menunjukkan cara menemukan nama skrip), dan merupakan contoh yang membingungkan dalam skrip yang sangat bermasalah.
Score_Under
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.