Distinguere tra l'esecuzione e l'approvvigionamento in uno script di shell bash?


22

O quello che sto chiedendo qui è estremamente poco ortodosso / non convenzionale / rischioso, o le mie abilità su Google Fu non sono proprio all'altezza ...

In uno bashscript di shell, c'è un modo semplice per dire se viene fornito da un altro script di shell o è gestito da solo? In altre parole, è possibile distinguere tra i seguenti due comportamenti?

# from another shell script
source myScript.sh

# from command prompt, or another shell script
./myScript.sh

Quello che sto pensando di fare è creare uno script di shell simile a utility che contenga bashfunzioni che possono essere rese disponibili quando acquistate. Quando questo script viene eseguito da solo, tuttavia, mi piace che esegua determinate operazioni, anche in base alle funzioni definite. Esiste una specie di variabile d'ambiente su cui questo script di shell può prendere, ad es

some_function() {
    # ...
}
if [ -z "$IS_SOURCED" ]; then
    some_function;
fi

Preferibilmente, sto cercando una soluzione che non richiede lo script del chiamante per impostare le variabili flag.

modifica : conosco la differenza tra sourcing ed esecuzione dello script, cosa sto cercando di scoprire qui se è possibile dire la differenza nello script che viene utilizzato (in entrambi i modi).



@cuonglm ha modificato la mia domanda, conosco le differenze tra i due, ma mi chiedo se posso far sì che lo script della shell possa fare la differenza a livello di codice.
hjk,

4
@cuonglm: non un duplicato. Non sta chiedendo affatto il .comando, ma di rilevare se uno script è stato fornito o eseguito normalmente (ovvero in una subshell).
Jander il

Ottime risposte sulla stessa domanda allo stack overflow: stackoverflow.com/a/28776166/96944
Jannie Theunissen

Risposte:


19

Sì - la variabile $ 0 fornisce il nome dello script mentre veniva eseguito:

$ cat example.sh
#!/bin/bash
script_name=$( basename ${0#-} ) #- needed if sourced no path
this_script=$( basename ${BASH_SOURCE} )
if [[ ${script_name} = ${this_script} ]] ; then
    echo "running me directly"
else
    echo "sourced from ${script_name}"
fi 

$ cat example2.sh
#!/bin/bash
. ./example.sh

Che funziona come:

$ ./example.sh
running me directly
$ ./example2.sh
example.sh sourced from example2.sh

Ciò non soddisfa la provenienza da una shell interattiva, ma hai questa idea (spero).

Aggiornato per includere BASH_SOURCE - grazie hjk


Abbastanza vicino. :) Un piccolo ostacolo che dovrò specificare almeno il nome dello script, ma prenderò questa risposta se non ci sono altre soluzioni praticabili ...
hjk

7

Combinare la risposta di @ DarkHeart con la variabile d'ambiente BASH_SOURCEsembra fare il trucco:

$ head example*.sh
==> example2.sh <==
#!/bin/bash
. ./example.sh

==> example.sh <==
#!/bin/bash
if [ "$(basename $0)" = "$(basename $BASH_SOURCE)" ]; then
    echo "running directly"
else
    echo "sourced from $0"
fi
$ ./example2.sh
sourced from ./example2.sh
$ ./example.sh
running directly

modifica Sembra essere ancora una soluzione più semplice se dovessi contare solo il numero di elementi BASH_SOURCEnell'array:

if [ ${#BASH_SOURCE[@]} -eq 1 ]; then echo "running directly"; else echo "sourced from $0"; fi

1
Sembra che abbiamo trovato la variabile 'bash_source' allo stesso tempo. :)
DarkHeart,

@DarkHeart Ho aggiunto alla mia risposta l'uso del conteggio delle dimensioni dell'array ... grazie per avermi suggerito questo percorso! : D
hjk,

1

Ho appena creato lo stesso tipo di script di libreria che funziona molto come BusyBox. In esso, utilizzo la seguente funzione per verificare se è di provenienza ...

function isSourced () {
  [[ "${FUNCNAME[1]}" == "source" ]]  && return 0
  return 1
}

L'array FUNCNAME gestito da Bash è essenzialmente uno stack di chiamate di funzione. $FUNCNAME(o ${FUNCNAME[0]}) è il nome della funzione attualmente in esecuzione. ${FUNCNAME[1]}è il nome della funzione che lo ha chiamato e così via.

L'elemento più in alto è un valore speciale per lo script stesso. Conterrà ...

  • la parola "fonte" se lo script è di provenienza
  • la parola "principale" se lo script viene eseguito E il test viene eseguito all'interno di una funzione
  • "" (null) se lo script viene eseguito E il test viene eseguito al di fuori di qualsiasi funzione, ovvero ... a livello dello script stesso.

La funzione sopra in realtà funziona solo quando chiamata a livello di script (che è tutto ciò di cui avevo bisogno). Fallirebbe se chiamato dall'interno di un'altra funzione perché il numero di elemento dell'array sarebbe errato. Per farlo funzionare ovunque, è necessario trovare la parte superiore dello stack e testare quel valore, il che è più complicato.

Se necessario, è possibile ottenere il numero di elemento dell'array della "parte superiore dello stack" con ...

  local _top_of_stack=$(( ${#FUNCNAME[@]} - 1 ))

${#FUNCNAME[@]}è il numero di elementi nell'array. Come array a base zero, sottraggiamo 1 per ottenere l'ultimo articolo #.

Queste tre funzioni sono usate insieme per produrre una traccia di stack di funzioni simile a quella di Python e potrebbero darti un'idea migliore di come tutto questo funziona ...

function inspFnStack () {
  local T+="  "
  local _at=
  local _text="\n"
  local _top=$(inspFnStackTop)
  local _fn=${FUNCNAME[1]}; [[ $_fn =~ source|main ]]  || _fn+="()"
  local i=_top; ((--i))
  #
  _text+="$i item function call stack for $_fn ...\n"
  _text+="| L   BASH_SOURCE{BASH_LINENO called from}.FUNCNAME  \n"
  _text+="| ---------------------------------------------------\n"
  while (( $i > 0 ))
  do
    _text+="| $i ${T}$(inspFnStackItem $i)\n"
    T+="  "
    ((--i))
  done
  #
  printf "$_text\n"
  #
  return 0
}

function inspFnStackItem ()  {
  local _i=$1
  local _fn=${FUNCNAME[$_i]}; [[ $_fn =~ source|main ]]  || _fn+="()"
  local _at="${BASH_LINENO[$_i-1]}"; [[ $_at == 1 ]]  && _at="trap"
  local _item="${BASH_SOURCE[$_i]}{${_at}}.$_fn"
  #
  printf "%s" "$_item"
  return 0
}

function inspFnStackTop () {
  # top stack item is 1 less than length of FUNCNAME array stack
  printf "%d\n" $(( ${#FUNCNAME[@]} - 1 ))
  #
  return 0
}

Si noti che FUNCNAME, BASH_SOURCE e BASH_LINENO sono 3 array gestiti da bash come se fossero un array tridimensionale.


0

Voglio solo aggiungere che il conteggio dell'array sembra essere inaffidabile e probabilmente non si dovrebbe presumere che sia sourcestato usato poiché l'uso di un punto ( .) è anche molto comune (e precede la sourceparola chiave).

Ad esempio, per uno sourced.shscript contenente solo echo $0:


$ . sourced.sh 
bash
$ source sourced.sh 
bash
$ chmod +x sourced.sh 
$ ./sourced.sh 
./sourced.sh
$ cat ./sourced.sh 
echo $0

Le soluzioni di confronto suggeriscono di funzionare meglio.


0

Un modo che funziona anche durante l'approvvigionamento da una shell interattiva :

if [ $BASH_LINENO -ne 0 ]; then
    some_function;
fi

La BASH_LINENOvariabile è anche una matrice con tutte le linee in cui è stata eseguita la funzione chiamante. Sarà zero se si chiama direttamente lo script o un numero intero corrispondente a un numero di riga.

I documenti variabili BASH_ *

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.