2015-11-24 34 views
5

Ho uno script Bash che è stato originato. Quando questo script è originario, esegue una funzione nello script Bash. Questa funzione dovrebbe terminare lo script se una determinata condizione è abbinata. Come si può fare senza terminare la shell in cui viene eseguito lo script?Come uscire da una funzione di script Bash originario

Per essere chiari: voglio che l'azione di terminazione sia completata dalla funzione nello script di shell di origine, non nel corpo principale dello script di shell di origine. I problemi che riesco a vedere sono che return restituisce semplicemente dalla funzione al principale dello script mentre exit 1 termina la shell chiamante.

L'esempio seguente illustra il problema minimo:

main(){ 
    echo "starting test of environment..." 
    ensure_environment 
    echo "environment safe -- starting other procedures..." 
} 

ensure_environment(){ 
    if [ 1 == 1 ]; then 
     echo "environment problemm -- terminating..." 
     # exit 1 # <-- terminates calling shell 
     return # <-- returns only from function, not from sourced script 
    fi 
} 

main 
+1

Eseguire invece lo script. In questo modo, 'exit' uscirà dalla subshell in cui è in esecuzione. – fedorqui

+1

Interessante. Potresti dare un piccolo esempio per iniziare, che potrebbe essere migliorato. Per esempio. uno che termina la shell. –

+0

Chiedete qualcosa che sia impossibile. Devi ristrutturare il tuo codice. –

risposta

2

Questa è una ricetta su come raggiungere il tuo obiettivo con il tuo approccio. Non scriverò il tuo codice per te, descrivi solo come può essere fatto.

L'obiettivo è impostare/modificare le variabili di ambiente nella shell bash corrente mediante l'individuazione, in modo efficace, di uno script di shell possibilmente complesso. Alcuni componenti di questo script potrebbero decidere che l'esecuzione di questo script di origine dovrebbe interrompersi. Ciò che rende questo complicato è che questa decisione non è necessariamente di primo livello, ma può trovarsi in un'invocazione di una funzione annidata. return, quindi, non aiuta e exit termina la shell di sourcing, che non è desiderata.

Il vostro compito è reso più facile da questa dichiarazione di tuo:

ulteriore complessità che non posso davvero includere in un esempio minimo lo rende molto desiderabile per centralizzare la procedura di risoluzione in una funzione .

Ecco come si fa:

Invece di approvvigionamento tuo vero script che decide quale ambiente per impostare a quello che ("realscript.bash '), è fonte di un altro script' ipcscript.bash".

ipcscript.bash configurerà alcune comunicazioni tra processi. Questo potrebbe essere un pipe su qualche descrittore di file extra che apri con exec, potrebbe essere un file temporaneo, potrebbe essere qualcos'altro.

ipcscript.bash inizierà quindi realscript.bash come processo figlio. Ciò significa che l'ambiente cambia che realscript.bash per prima influisce solo sull'ambiente di tale istanza di processo figlio di bash. A partire da realscript.bash come un processo figlio, si ottiene la possibilità di terminare l'esecuzione a qualsiasi livello annidato con exit senza terminare la shell di sourcing.

La vostra chiamata per uscire vivrà, come si scrive, in una funzione centralizzata che viene chiamata da qualsiasi livello quando viene presa una decisione per terminare l'esecuzione. La tua funzione di terminazione ora ha bisogno, prima di uscire, di scrivere l'ambiente corrente sul meccanismo IPC in un formato adatto.

ipcscript.bash leggerà le impostazioni dell'ambiente dal meccanismo IPC e riprodurrà tutte le impostazioni nel processo della shell di sourcing.

7

È possibile return da uno script di shell source. POSIX spec

Così, mentre non si return dalla funzione può direttamente per ottenere ciò che si vuole possibile ritorno dal corpo principale dello script se la funzione restituisce non-zero (o qualche altro concordato valore).

Ad esempio:

$ cat foo.sh 
f() { 
    echo in f "[email protected]" 
} 

e() { 
    return 2 
} 

f 1 
e 
f 2 
if ! e; then 
    return 
fi 
f 3 
$ . foo.sh 
in f 1 
in f 2 
+1

Grazie per la soluzione. Sono consapevole di questo approccio. Il mio scopo è quello di trovare un modo per terminare lo script originario all'interno di una delle sue funzioni, perché una complessità aggiuntiva che non posso veramente includere in un esempio minimale rende molto desiderabile centralizzare la procedura di terminazione in una funzione. – d3pd

+0

@ d3pd Non è possibile. Non senza un sub-shell. Semplicemente non è possibile. Con questo però puoi mantenere qualsiasi logica tu voglia nella funzione. Hai solo bisogno di chiamare la funzione in un 'if' o' ensure_environment || ritorno o qualcosa del genere. .... Potresti * essere in grado di fare qualcosa con una trappola 'RETURN' che controlla' $? 'O qualunque altra variabile in realtà. –

2

ne dite di questo: Chiamare tutto attraverso un semplice involucro, qui "ocall", che mantiene uno stato globale, qui "STILL_OK"

STILL_OK=true 

ocall() { 
    if $STILL_OK 
    then 
     echo -- "[email protected]" # this is for debugging, you can delete this line 
     if "[email protected]" 
     then 
      true 
     else 
      STILL_OK=false 
     fi 
    fi 
} 

main(){ 
    ocall echo "starting test of environment..." 
    ocall ensure_environment 
    ocall echo "environment safe -- starting other procedures..." 
} 

ensure_environment(){ 
    if [ 1 == 1 ]; then 
     ocall echo "environment problemm -- terminating..." 
     # exit 1 # <-- terminates calling shell 
     return 1 # <-- returns from sourced script but leaves sourcing shell running 
    fi 
} 

ocall main 
+0

Affinché '$ @' mantenga le sue proprietà, dovrebbe essere sempre citato a doppia virgola. Non ha molta importanza qui, ma per me, fa partire immediatamente un allarme per principianti, quindi potresti decidere di aggiustarlo solo per questo motivo: – tripleee

+0

Giusto.Evito bash quando posso usare ruby. Corretto. –

+0

Nitpicking: 'se $ STILL_OK' è un punto debole della sicurezza. Meglio usare 'if [" $ STILL_OK "=" true "]' anche se non è elegante. Altrimenti, nel caso in cui si implementi qualsiasi percorso che porta a questa linea di origine senza impostare 'STILL_OK', si eseguirà qualsiasi cosa sia attualmente impostata nel proprio ambiente per' STILL_OK'. Probabilmente nulla se non lo si sa mai. – Alfe

2

Non è possibile.

Se si genera uno script, è (per gli aspetti interessati qui) come immettere ciascuna riga una ad una nella shell di chiamata (sorgente). Vuoi lasciare un ambito (lo script di origine) che non esiste, quindi non può essere lasciato.

L'unico modo che posso pensare è passando l'uscita-desiderano tornare alla funzione di chiamata e controllo per esso:

main() { 
    echo "starting test of environment..." 
    [ "$(ensure_environment)" = "bailout" ] && return 
    echo "environment safe -- starting other procedures..." 
} 

ensure_environment() { 
    if [ 1 == 1 ]; then 
     echo "bailout" 
     return 
    fi 
} 

main 

Cosa chiedete, inoltre, non è in genere possibile in altre lingue. Normalmente ogni funzione può solo terminare se stessa (ritornando), non un ambito definito più ampio al di fuori di se stesso (come uno script in cui risiede). An exception to this rule is exception handling utilizzando try/catch o simili.

Considerate anche questo: se si genera questo script, le funzioni della shell diventano note nella shell di sourcing. Quindi puoi chiamarli più tardi. Quindi (di nuovo) non esiste un ambito circostante che la funzione potrebbe terminare.

0

A volte scrivo uno script con funzioni a portata di mano che desidero utilizzare al di fuori dello script. In questo caso, se lo script viene eseguito, allora fa la sua cosa. Ma se lo script è originario, carica solo alcune funzioni nella shell di sourcing. Io uso questo modulo:

#!/bin/bash 

# This function will be sourcable 
foo() { 
    echo hello world 
} 

# end if being sourced 
if [[ $0 == bash ]]; then 
    return 
fi 

# the rest of the script goes here