Come faccio a sapere il nome del file di script in uno script Bash?


606

Come posso determinare il nome del file di script Bash all'interno dello script stesso?

Come se il mio script fosse nel file runme.sh, come potrei farlo per visualizzare il messaggio "You run running runme.sh" senza codificarlo?


3
Simile [Uno script bash può dire in quale directory è memorizzato?] (Su stackoverflow.com/questions/59895/… )
Rodrigue

Risposte:


631
me=`basename "$0"`

Per leggere un link simbolico 1 , che di solito non è quello che vuoi (di solito non vuoi confondere l'utente in questo modo), prova:

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

IMO, produrrà risultati confusi. "Ho eseguito foo.sh, ma sta dicendo che sto eseguendo bar.sh !? Deve essere un bug!" Inoltre, uno degli scopi di avere collegamenti simbolici con nomi diversi è quello di fornire funzionalità diverse in base al nome che si chiama come (pensa gzip e gunzip su alcune piattaforme).


1 Cioè, per risolvere i collegamenti simbolici in modo tale che quando l'utente esegue foo.shun collegamento simbolico bar.sh, si desidera utilizzare il nome risolto bar.shanziché foo.sh.


40
$ 0 ti dà il nome tramite il quale è stato invocato lo script, non il percorso reale del file di script effettivo.
Chris Conway,

4
Funziona a meno che non vieni chiamato tramite symlink. Ma, anche allora, di solito è quello che vuoi, IME.
Tanktalus,

78
Non funziona per gli script che provengono anziché invocati.
Charles Duffy,


8
-1, 1. readlink viaggerà solo per un symlink in profondità, 2. $0nel primo esempio è soggetto alla divisione di parole, 3. $0viene passato a basename, readlink ed eco in una posizione che gli consente di essere trattato come un interruttore della riga di comando . Suggerisco invece me=$(basename -- "$0")o in modo più efficiente a spese della leggibilità, me=${0##*/}. Per i link simbolici, me=$(basename -- "$(readlink -f -- "$0")")supponendo gnu utils, altrimenti sarà uno script molto lungo che non scriverò qui.
Score_Under

246
# ------------- SCRIPT ------------- # # ------------- CHIAMATO ------ ------- #

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



# Nota sulla riga successiva, il primo argomento viene chiamato tra virgolette doppie, # e singole, poiché contiene due parole


$   / misc / shell_scripts / check_root / show_parms . sh "'ciao lì'" "'william'" 

# ------------- RISULTATI ------------- #

# argomenti chiamati con ---> 'ciao a tutti' 'william' # $ 1 ----------------------> 'ciao a tutti' # $ 2 ---- ------------------> 'william' # percorso per me --------------> / misc / shell_scripts / check_root / show_parms. sh # percorso principale -------------> / misc / shell_scripts / check_root # my name -----------------> show_parms.sh






# ------------- FINE ------------- #

11

1
Come posso ottenere solo show_paramsil nome senza estensione opzionale?
Acumenus,

Non funziona nel caso in cui lo script venga richiamato da un'altra cartella. Il percorso è incluso in ${0##*/}. Testato usando GitBash.
AlikElzin-Kilaka,

1
Quanto sopra non ha funzionato per me da uno script .bash_login, ma la soluzione Dimitre Radoulov di $ BASH_SOURCE funziona alla grande.
Giovanni,

@ Giovanni Sicuramente intendi .bash_profile ? O in alternativa .bashrc ? Dovresti sapere però che ci sono alcune sottili differenze nel modo in cui sono invocate / provenienti. Sono stanco morto, ma se sto pensando bene sarebbe probabilmente il problema. Un posto che importa è se si accede a un sistema in remoto tramite ad esempio ssh rispetto al sistema locale.
Pryftan,

193

Con bash> = 3 i seguenti lavori:

$ ./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
Grande! Questa è la risposta che funziona sia per ./scrip.sh che per i sorgenti ./script.sh
zhaorufei,

4
Questo è quello che voglio, ed è facile usare " dirname $BASE_SOURCE" per ottenere la directory in cui si trovano gli script.
Larry Cai,

2
Ho quasi imparato questa differenza nel modo più duro quando ho scritto una sceneggiatura auto-eliminante. Fortunatamente "rm" era alias di "rm -i" :)
kervin,

comunque al. ./s per ottenere il nome ./s invece di bash? Ho scoperto che $ 1 non è sempre impostato su ./s ...
David Mokon Bond

1
È in BASH_SOURCE , vero?
Dimitre Radoulov,

69

$BASH_SOURCE fornisce la risposta corretta durante l'approvvigionamento dello script.

Ciò include tuttavia il percorso, quindi per ottenere solo il nome file degli script, utilizzare:

$(basename $BASH_SOURCE) 

2
Questa è la risposta IMHO migliore perché la soluzione si avvale di codice autodocumentante. $ BASH_SOURCE è totalmente comprensibile senza leggere alcuna documentazione mentre ad esempio $ {0 ## * /} non lo è
Andreas M. Oberheim,

Questa risposta è di maggior valore credo perché se corriamo come . <filename> [arguments], $0darebbe il nome della shell del chiamante. Beh, almeno su OSX di sicuro.
Mihir,

68

Se il nome dello script ha spazi in esso, un modo più robusto è quello di utilizzare "$0"o "$(basename "$0")"- o su MacOS: "$(basename \"$0\")". Ciò impedisce che il nome venga alterato o interpretato in alcun modo. In generale, è buona norma citare sempre due volte i nomi delle variabili nella shell.


2
+1 per la risposta diretta + conciso. se ho bisogno di funzionalità di collegamento simbolico ti suggerisco di vedere: la risposta di Travis B. Hartwell.
Trevor Boyd Smith,

26

Se lo desideri senza il percorso, lo useresti ${0##*/}


E se lo volessi senza alcuna estensione di file opzionale?
Acumenus,

Per rimuovere un'estensione, puoi provare "$ {VARIABLE% .ext}" dove VARIABLE è il valore ottenuto da $ {0 ## * /} e ".ext" è l'estensione che desideri rimuovere.
BrianV,

19

Per rispondere a Chris Conway , su Linux (almeno) faresti questo:

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

readlink stampa il valore di un collegamento simbolico. Se non è un collegamento simbolico, stampa il nome del file. -n gli dice di non stampare una nuova riga. -f gli dice di seguire completamente il link (se un link simbolico fosse un link ad un altro link, lo risolverebbe anche quello).


2
-n è innocuo ma non necessario perché la struttura $ (...) lo taglierà.
zhaorufei,

16

Ho trovato che questa linea funziona sempre, indipendentemente dal fatto che il file provenga o venga eseguito come script.

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

Se si desidera seguire i collegamenti simbolici, utilizzare readlinkil percorso sopra riportato, in modo ricorsivo o non ricorsivo.

Il motivo per cui il one-liner funziona è spiegato dall'uso della BASH_SOURCEvariabile d'ambiente e del suo associato FUNCNAME.

BASH_SOURCE

Una variabile di matrice i cui membri sono i nomi dei file di origine in cui sono definiti i nomi delle funzioni shell corrispondenti nella variabile di matrice FUNCNAME. La funzione shell $ {FUNCNAME [$ i]} è definita nel file $ {BASH_SOURCE [$ i]} e chiamata da $ {BASH_SOURCE [$ i + 1]}.

FUNCNAME

Una variabile di matrice contenente i nomi di tutte le funzioni della shell attualmente nello stack delle chiamate di esecuzione. L'elemento con indice 0 è il nome di qualsiasi funzione shell attualmente in esecuzione. L'elemento più in basso (quello con l'indice più alto) è "principale". Questa variabile esiste solo quando è in esecuzione una funzione shell. Le assegnazioni a FUNCNAME non hanno alcun effetto e restituiscono uno stato di errore. Se FUNCNAME non è impostato, perde le sue proprietà speciali, anche se viene successivamente ripristinato.

Questa variabile può essere utilizzata con BASH_LINENO e BASH_SOURCE. Ogni elemento di FUNCNAME ha elementi corrispondenti in BASH_LINENO e BASH_SOURCE per descrivere lo stack di chiamate. Ad esempio, $ {FUNCNAME [$ i]} è stato chiamato dal file $ {BASH_SOURCE [$ i + 1]} al numero di riga $ {BASH_LINENO [$ i]}. Il chiamante incorporato visualizza lo stack di chiamate corrente utilizzando queste informazioni.

[Fonte: manuale di Bash]


2
Funziona se provi in ​​un file a (supponi che ail contenuto sia echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}") da una sessione interattiva - quindi ti darà ail percorso. Ma se scrivi uno script bcon source adentro ed esegui ./b, ritornerà bil percorso.
PSkocik,

12

Queste risposte sono corrette per i casi che indicano, ma c'è ancora un problema se si esegue lo script da un altro script usando la parola chiave 'source' (in modo che venga eseguito nella stessa shell). In questo caso, ottieni $ 0 dello script chiamante. E in questo caso, non credo sia possibile ottenere il nome dello script stesso.

Questo è un caso limite e non dovrebbe essere preso TROPPO sul serio. Se esegui lo script direttamente da un altro script (senza "sorgente"), l'utilizzo di $ 0 funzionerà.


2
Hai un ottimo punto. Non un caso limite IMO. C'è una soluzione però: vedi la risposta di Dimitre Radoulov sopra
Serge Wautier,

10

Poiché alcuni commenti hanno chiesto il nome del file senza estensione, ecco un esempio su come farlo:

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

Godere!


9

Ri: Risposta di Tanktalus (accettata) sopra, un modo leggermente più pulito è usare:

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

Se il tuo script proviene da un altro script bash, puoi utilizzare:

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

Concordo sul fatto che sarebbe difficile confondere i link simbolici se il tuo obiettivo è fornire feedback all'utente, ma ci sono occasioni in cui è necessario ottenere il nome canonico in uno script o in un altro file, e questo è il modo migliore, imo.


1
Grazie per i sourcenomi degli script.
Fredrick Gauss, l'

8

Puoi usare $ 0 per determinare il nome del tuo script (con il percorso completo) - per ottenere il nome dello script solo tu puoi tagliare quella variabile

basename $0

8

se il tuo invocare lo script shell piace

/home/mike/runme.sh

$ 0 è il nome completo

 /home/mike/runme.sh

basename $ 0 otterrà il nome del file di base

 runme.sh

e devi inserire questo nome di base in una variabile come

filename=$(basename $0)

e aggiungi il tuo testo aggiuntivo

echo "You are running $filename"

così piacciono i tuoi script

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

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

Questo risolve i collegamenti simbolici (realpath lo fa), gestisce gli spazi (le doppie virgolette lo fanno) e troverà il nome corrente dello script anche quando è di provenienza (. ./Myscript) o chiamato da altri script ($ BASH_SOURCE lo gestisce). Dopotutto, è bene salvarlo in una variabile d'ambiente per riutilizzarlo o per copiarlo altrove (this =) ...


3
FYI realpathnon è un comando BASH integrato. È un eseguibile autonomo disponibile solo in alcune distribuzioni
StvnW

4

In bashè possibile ottenere il nome del file di script utilizzando $0. In genere $1, $2ecc. Devono accedere agli argomenti della CLI. Allo stesso modo $0è accedere al nome che attiva lo script (nome del file di script).

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

Se invochi lo script con percorso come /path/to/script.shallora, $0anche il nome file con percorso verrà assegnato. In tal caso è necessario utilizzare $(basename $0)per ottenere solo il nome del file di script.


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

2

$0non risponde alla domanda (come ho capito). Una dimostrazione:

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

Come si ./linktoscriptstampa script.sh?

[EDIT] Per @ephemient nei commenti sopra, sebbene la cosa del collegamento simbolico possa sembrare inventata, è possibile giocherellare in $0modo tale da non rappresentare una risorsa del filesystem. L'OP è un po 'ambiguo su ciò che voleva.


ho aggiunto la risposta alla mia precedente risposta, ma non sono d'accordo sul fatto che questo è ciò che realmente si desidera (o che si dovrebbe desiderare, salvo alcune circostanze attenuanti che attualmente non riesco a capire)
Tanktalus,

2
Penso che stampare linktoscript invece di script.sh sia una caratteristica, non un bug. Diversi comandi unix usano il loro nome per alterare il loro comportamento. Un esempio è vi / ex / view.
mouviciel,

@Tanktalus: ci sono casi in cui vuoi che il comportamento cambi in base alla posizione reale del file - su un progetto precedente avevo uno script "Ramo attivo", che collegava al percorso diversi strumenti dal ramo che ero lavorandoci; quegli strumenti potevano quindi scoprire in quale directory erano stati estratti per essere eseguiti con i file giusti.
Andrew Aylett,

2

Informazioni grazie a Bill Hernandez. Ho aggiunto alcune preferenze che sto adottando.

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

Saluti


1

Breve, chiaro e semplice, in my_script.sh

#!/bin/bash

running_file_name=$(basename "$0")

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

Produzione:

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


0

Ecco cosa mi è venuto in mente, ispirato alla risposta di Dimitre Radoulov (che ho votato, comunque) .

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

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

indipendentemente dal modo in cui chiami la tua sceneggiatura

. path/to/script.sh

o

./path/to/script.sh


-6

qualcosa del genere?

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, non risponde alla domanda (non mostra come trovare il nome dello script) ed è un esempio confuso in uno script con errori.
Score_Under
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.