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?

Ma99uS
źródło
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.

Tanktalus
źródło
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 ------------- #
Bill Hernandez
źródło
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"
Dimitre Radoulov
źródło
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) 
Zainka
źródło
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.

Josh Lee
źródło
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##*/}

Pan Muskrat
źródło
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ż).

Travis B. Hartwell
źródło
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]

gkb0986
źródło
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ć.

Jim Dodd
źródło
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ę!

Simon Mattes
źródło
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.

Szymon
źródło
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
VolkA
źródło
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"
LawrenceLi
źródło
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 =) ...

jcalfee314
źródło
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.

rashok
źródło
3
echo "$(basename "`test -L ${BASH_SOURCE[0]} \
                   && readlink ${BASH_SOURCE[0]} \
                   || echo ${BASH_SOURCE[0]}`")"
ecwpz91
źródło
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ł.

Chris Conway
źródło
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

linxuser
źródło
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.
Bơ Loong A Nhứi
źródło
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
Salathiel Genèse
źródło
-1

echo „Używasz 0 $”

mmacaulay
źródło
To nie jest skrypt bash, jest pełna ścieżka.
e-info128
-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
hynt
źródło
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