Jak poznać nazwę pliku skryptu w skrypcie Bash?


605

Jak mogę określić nazwę pliku skryptu Bash w samym skrypcie?

Na przykład, jeśli mój skrypt znajduje się w pliku runme.sh, to jak sprawiłbym, aby wyświetlał komunikat „Używasz pliku runme.sh” bez tego?


3
Podobne [Czy skrypt bash może powiedzieć, w którym katalogu jest przechowywany?] (Do stackoverflow.com/questions/59895/… )
Rodrigue

Aby uzyskać informacje na temat katalogu, zobacz: Pobieranie katalogu źródłowego skryptu Bash od wewnątrz .
kenorb

Odpowiedzi:


630
me=`basename "$0"`

Aby odczytać dowiązanie symboliczne 1 , które zwykle nie jest tym, czego chcesz (zwykle nie chcesz w ten sposób mylić użytkownika), spróbuj:

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

IMO, co spowoduje zamieszanie. „Uruchomiłem foo.sh, ale to mówi, że prowadzę bar.sh !? To musi być błąd!” Poza tym jednym z celów posiadania dowiązań symbolicznych o różnych nazwach jest zapewnienie różnych funkcji opartych na nazwie, którą nazywa się jako (na niektórych platformach używaj gzip i gunzip).


1 Oznacza to, że aby rozwiązywać dowiązania symboliczne w taki sposób, że gdy użytkownik wykonuje foo.shfaktycznie dowiązanie symboliczne bar.sh, wolisz użyć rozpoznanej nazwy bar.shzamiast foo.sh.


40
$ 0 daje nazwę, za pomocą której skrypt został wywołany, a nie rzeczywistą ścieżkę rzeczywistego pliku skryptu.
Chris Conway,

4
Działa, chyba że zostaniesz wywołany przez dowiązanie symboliczne. Ale nawet wtedy zwykle jest to, czego chcesz, IME.
Tanktalus,

78
Nie działa dla skryptów, które są pozyskiwane w przeciwieństwie do wywoływanych.
Charles Duffy

1
@Charles Duffy: Zobacz odpowiedź Dimitra Radoulova poniżej .
Serge Wautier

8
-1, 1. readlink przenosi tylko jedno głębokie dowiązanie symboliczne, 2. $0w pierwszym przykładzie podlega $0podziałowi słów, 3. jest przekazywany do basename, readlink i echa w pozycji, która pozwala na traktowanie go jako przełącznika linii poleceń . Proponuję zamiast me=$(basename -- "$0")lub bardziej efektywnie kosztem czytelności me=${0##*/}. W przypadku dowiązań symbolicznych, me=$(basename -- "$(readlink -f -- "$0")")zakładając , że GNU utils, w przeciwnym razie będzie to bardzo długi skrypt, którego tutaj nie napiszę.
Score_Under

246
# ------------- PISMO ------------- # # ------------- WEZWANE ------ ------- #

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



# Uwaga w następnym wierszu pierwszy argument jest wywoływany w obrębie podwójnego, # i pojedynczego cudzysłowu, ponieważ zawiera dwa słowa


$   / misc / shell_scripts / check_root / show_parms . sh „„ hello there ” ” „william” ” 

# ------------- WYNIKI ------------- #

# argumenty wywołane z ---> 'hello there' 'william' # $ 1 ----------------------> 'hello there' # $ 2 ---- ------------------> 'william' # ścieżka do mnie --------------> / misc / shell_scripts / check_root / show_parms. sh # ścieżka nadrzędna -------------> / misc / shell_scripts / check_root # moje imię -----------------> show_parms.sh






# ------------- KONIEC ------------- #

11
@NickC patrz Usuwanie
podciągów

1
Jak uzyskać po prostu show_params, tj. Nazwę bez opcjonalnego rozszerzenia?
Acumenus

Nie działa w przypadku wywołania skryptu z innego folderu. Ścieżka jest zawarta w ${0##*/}. Testowane przy użyciu GitBash.
AlikElzin-kilaka

1
Powyższe nie działało dla mnie ze skryptu .bash_login, ale rozwiązanie Dimitre Radoulov z $ BASH_SOURCE działa świetnie.
Jan

@John Na pewno masz na myśli .bash_profile ? Lub alternatywnie .bashrc ? Powinieneś jednak wiedzieć, że istnieją pewne subtelne różnice w sposobie wywoływania / pozyskiwania. Jestem bardzo zmęczony, ale jeśli dobrze się nad tym zastanowić, to prawdopodobnie problem. Jedno miejsce ma znaczenie, jeśli logujesz się zdalnie do systemu np. Ssh w porównaniu z systemem lokalnym.
Pryftan

193

Przy bash> = 3 działają następujące funkcje:

$ ./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
Świetny! Oto odpowiedź, która działa zarówno na ./scrip.sh, jak i source ./script.sh
zhaorufei,

4
Tego właśnie chcę i można łatwo użyć „ dirname $BASE_SOURCE”, aby uzyskać katalog, w którym znajdują się skrypty.
Larry Cai,

2
Prawie nauczyłem się tej różnicy na własnej skórze podczas pisania skasowanego skryptu. Na szczęście „rm” zostało aliasowane do „rm -i” :)
kervin

w każdym razie do. ./s, aby uzyskać nazwę ./s zamiast bash? Odkryłem, że 1 $ nie zawsze jest ustawiony na ./s ...
David Mokon Bond

1
Jest w BASH_SOURCE , prawda?
Dimitre Radoulov

69

$BASH_SOURCE daje poprawną odpowiedź przy pozyskiwaniu skryptu.

Obejmuje to jednak ścieżkę, więc aby uzyskać tylko nazwę pliku skryptu, użyj:

$(basename $BASH_SOURCE) 

2
Ta odpowiedź to IMHO najlepsza, ponieważ rozwiązanie wykorzystuje kod samokontrujący. $ BASH_SOURCE jest całkowicie zrozumiałe bez czytania jakiejkolwiek dokumentacji, podczas gdy np. $ {0 ## * /} nie jest
Andreas M. Oberheim 6'18

Wierzę, że ta odpowiedź ma większą wartość, ponieważ jeśli działamy tak . <filename> [arguments], podamy $0nazwę powłoki wywołującej. Na pewno przynajmniej na OSX.
Mihir

68

Jeśli nazwa zawiera spacje skrypt w nim bardziej wytrzymałe sposobem jest użycie "$0"albo "$(basename "$0")"- albo na MacOS: "$(basename \"$0\")". Zapobiega to zniekształcaniu lub interpretowaniu nazwy w jakikolwiek sposób. Ogólnie dobrą praktyką jest zawsze podwójne cytowanie nazw zmiennych w powłoce.


2
+1 za bezpośrednią odpowiedź + zwięzłe. jeśli potrzebuję funkcji dowiązań symbolicznych, sugeruję zobaczyć: odpowiedź Travisa B. Hartwella.
Trevor Boyd Smith,

26

Jeśli chcesz bez ścieżki, skorzystaj ${0##*/}


A co, jeśli chcę bez opcjonalnego rozszerzenia pliku?
Acumenus

Aby usunąć rozszerzenie, możesz wypróbować „$ {VARIABLE% .ext}”, gdzie VARIABLE to wartość otrzymana z $ {0 ## * /}, a „.ext” to rozszerzenie, które chcesz usunąć.
BrianV

19

Aby odpowiedzieć Chrisowi Conwayowi , w Linuksie (przynajmniej) zrobiłbyś to:

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

readlink drukuje wartość dowiązania symbolicznego. Jeśli nie jest dowiązaniem symbolicznym, drukuje nazwę pliku. -n mówi, aby nie drukował nowego wiersza. -f mówi mu, aby podążał za linkiem całkowicie (jeśli dowiązanie symboliczne było linkiem do innego linku, rozwiązałoby to również).


2
-n jest nieszkodliwy, ale nie jest konieczny, ponieważ struktura $ (...) go przycina.
zhaorufei,

16

Przekonałem się, że ta linia zawsze działa, niezależnie od tego, czy plik jest pobierany czy uruchamiany jako skrypt.

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

Jeśli chcesz podążać za dowiązaniami symbolicznymi, użyj readlinkścieżki powyżej, rekurencyjnie lub nierekurencyjnie.

Powód, dla którego działa jednolinijka, jest wyjaśniony przez użycie BASH_SOURCEzmiennej środowiskowej i jej powiązania FUNCNAME.

BASH_SOURCE

Zmienna tablicowa, której członkowie są źródłowymi nazwami plików, w których zdefiniowane są odpowiednie nazwy funkcji powłoki w zmiennej tablicowej FUNCNAME. Funkcja powłoki $ {FUNCNAME [$ i]} jest zdefiniowana w pliku $ {BASH_SOURCE [$ i]} i wywołana z $ {BASH_SOURCE [$ i + 1]}.

FUNCNAME

Zmienna tablicowa zawierająca nazwy wszystkich funkcji powłoki znajdujących się obecnie na stosie wywołań wykonawczych. Element o indeksie 0 jest nazwą każdej aktualnie wykonywanej funkcji powłoki. Najniższy element (ten o najwyższym indeksie) to „main”. Ta zmienna istnieje tylko podczas wykonywania funkcji powłoki. Przypisania do FUNCNAME nie działają i zwracają status błędu. Jeśli FUNCNAME jest rozbrojony, traci swoje specjalne właściwości, nawet jeśli jest następnie resetowany.

Tej zmiennej można używać z BASH_LINENO i BASH_SOURCE. Każdy element FUNCNAME ma odpowiadające elementy w BASH_LINENO i BASH_SOURCE do opisania stosu wywołań. Na przykład $ {FUNCNAME [$ i]} został wywołany z pliku $ {BASH_SOURCE [$ i + 1]} w linii $ {BASH_LINENO [$ i]}. Wbudowane narzędzie wywołujące wyświetla bieżący stos połączeń przy użyciu tych informacji.

[Źródło: instrukcja Bash]


2
Działa, jeśli źródło w pliku a (zakładając a, że zawartość jest echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}") z sesji interaktywnej - poda aścieżkę. Ale jeśli napiszesz skrypt bz source anim i uruchomisz ./b, zwróci on bścieżkę.
PSkocik

12

Te odpowiedzi są poprawne dla przypadków, które podają, ale nadal występuje problem, jeśli skrypt zostanie uruchomiony z innego skryptu przy użyciu słowa kluczowego „source” (tak, aby działał w tej samej powłoce). W takim przypadku otrzymasz 0 $ skryptu wywołującego. W tym przypadku nie sądzę, że można uzyskać nazwę samego skryptu.

Jest to przypadek skrajny i nie należy go traktować zbyt poważnie. Jeśli uruchomisz skrypt bezpośrednio z innego skryptu (bez „źródła”), użycie $ 0 będzie działać.


2
Masz bardzo dobry punkt. To nie jest przypadek IMO. Istnieje jednak rozwiązanie: patrz odpowiedź Dymitra Radoulova powyżej
Serge Wautier

10

Ponieważ niektóre komentarze dotyczyły nazwy pliku bez rozszerzenia, oto przykład, jak to zrobić:

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

Cieszyć się!


9

Re: Odpowiedź Tanktalusa (zaakceptowana) powyżej, nieco czystszym sposobem jest użycie:

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

Jeśli twój skrypt pochodzi z innego skryptu bash, możesz użyć:

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

Zgadzam się, że dezorientacja dowiązań symbolicznych byłaby myląca, jeśli Twoim celem jest przekazanie informacji zwrotnej użytkownikowi, ale zdarzają się sytuacje, w których musisz uzyskać kanoniczną nazwę skryptu lub innego pliku, i to jest najlepszy sposób, imo.


1
Dzięki za sourced nazw skryptów.
Fredrick Gauss,

8

Możesz użyć 0 $, aby określić nazwę skryptu (z pełną ścieżką) - aby uzyskać nazwę skryptu, możesz tylko przyciąć tę zmienną

basename $0

8

jeśli wywołujesz skrypt powłoki jak

/home/mike/runme.sh

$ 0 to pełna nazwa

 /home/mike/runme.sh

basename $ 0 otrzyma podstawową nazwę pliku

 runme.sh

i musisz umieścić tę podstawową nazwę w zmiennej takiej jak

filename=$(basename $0)

i dodaj swój dodatkowy tekst

echo "You are running $filename"

tak jak twoje skrypty

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

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

To rozwiązuje dowiązania symboliczne (robi to realpath), obsługuje spacje (robią to podwójne cudzysłowy) i znajdzie bieżącą nazwę skryptu, nawet gdy jest pozyskiwany (. ./Myscript) lub wywoływany przez inne skrypty ($ BASH_SOURCE obsługuje to). Po tym wszystkim dobrze jest zapisać to w zmiennej środowiskowej do ponownego użycia lub łatwego kopiowania w innym miejscu (this =) ...


3
FYI realpathnie jest wbudowanym poleceniem BASH. Jest to samodzielny plik wykonywalny, który jest dostępny tylko w niektórych dystrybucjach
StvnW

4

W bashmożna uzyskać nazwę pliku skryptu za pomocą $0. Generalnie $1, $2etc są do argumentów dostęp CLI. Podobnie $0jest uzyskać dostęp do nazwy, która wyzwala skrypt (nazwa pliku skryptu).

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

Jeśli wywołasz skrypt ze ścieżką podobną, /path/to/script.shwówczas $0również poda nazwę pliku ze ścieżką. W takim przypadku należy użyć, $(basename $0)aby uzyskać tylko nazwę pliku skryptu.


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

2

$0nie odpowiada na pytanie (jak rozumiem). Demonstracja:

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

Jak ./linktoscriptmożna wydrukować script.sh?

[EDYCJA] Na @ephemient w komentarzach powyżej, chociaż łącze symboliczne może wydawać się wymyślone, można manipulować $0tak, aby nie reprezentował zasobu systemu plików. OP jest nieco niejednoznaczny co do tego, czego chciał.


dodałem odpowiedź na to pytanie do mojej powyższej odpowiedzi, ale nie zgadzam się z tym, że to jest to, czego naprawdę chcieliśmy (lub że powinieneś tego chcieć, z wyjątkiem okoliczności łagodzących, których obecnie nie mogę pojąć)
Tanktalus 10.1008

2
Wydaje mi się, że drukowanie linktoscript zamiast script.sh to funkcja, a nie błąd. Kilka poleceń uniksowych używa swojej nazwy do zmiany ich zachowania. Przykładem jest vi / ex / view.
mouviciel

@Tanktalus: Są przypadki, w których chcesz zmienić zachowanie w zależności od rzeczywistej lokalizacji pliku - w poprzednim projekcie miałem skrypt „Active branch”, który mógłby dowiązać do ścieżki kilku narzędzi z gałęzi, w której byłem pracować nad; narzędzia te mogłyby następnie dowiedzieć się, w którym katalogu zostały sprawdzone, aby uruchomić odpowiednie pliki.
Andrew Aylett,

2

Informacje dzięki Billowi Hernandezowi. Dodałem pewne preferencje, które przyjmuję.

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

Twoje zdrowie


1

Krótkie, jasne i proste, w my_script.sh

#!/bin/bash

running_file_name=$(basename "$0")

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

Wynik:

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


0

Oto co wymyśliłem, zainspirowany Dimitre Radoulov odpowiedź „s (co upvoted, nawiasem mówiąc) .

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

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

niezależnie od sposobu wywołania skryptu

. path/to/script.sh

lub

./path/to/script.sh


-6

coś takiego?

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, nie odpowiada na pytanie (nie pokazuje, jak znaleźć nazwę skryptu) i jest mylącym przykładem w bardzo błędnym skrypcie.
Score_Under
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.