reperimento di uno script Bash - Return on Error, anziché Exit?


17

Sto cercando uno script bash nel terminale , quindi esco per errore con

set -o errexit

uccide il mio terminale, che è ESTREMAMENTE ANNOYING, perché devo chiudere il terminale, aprirne un altro e resettare alcune variabili.

Finora, usando

command || return

le righe, nella sceneggiatura, stanno facendo esattamente quello che voglio

set -o errexit

fare ... Ma lo voglio fatto per l'intera sceneggiatura; non solo una riga / comando

Ho un file pieno di comandi per la configurazione di un sito e preferirei non eseguire il comando || ritorno

per ogni singola riga nel file

C'è un'altra opzione impostata o qualcos'altro che "restituirà" invece di uscire dal terminale?

- Solo per chiarezza , mi piacerebbe uccidere lo script e lasciare il terminale nello stesso stato che premendo Ctrl + C per uccidere un servizio in esecuzione nel terminale. command || returnfa quello. Ma non voglio attaccare || returnad ogni riga del file. Quindi sto cercando qualcosa di simile a set -o errexit, che non causi l'arresto del terminale

--- Nota: creazione di uno script stupido con due righe (super.sh):

create_path=~/Desktop/site_builder/create.sh
source $create_path blah

E posizionandolo set -o errexitnella parte superiore di create.sh,

funziona esattamente come me lo aspetto. Tuttavia, è davvero stupido dover creare un file con due righe, solo per chiamare un altro script bash, invece di chiamarlo semplicemente dal terminale. Ugghhh

ecco alcuni esempi:

in super.sh

#!/bin/bash

create_path=~/Desktop/site_builder/create.sh
source $create_path blah

in create.sh

#!/bin/bash
set -o errexit
#line below this is a line that fails and will cause the script to stop and return to the terminal as expected 
sed "s/@@SITE_NAME@@/$dirname" 
~/Desktop/site_builder/template_files/base.html > ~/Desktop/$dirname/templates/base.html # a line with a stupid error

nel terminal:

 $ bash super.sh

uscita come previsto:

my-mac$

Questo funziona Che soluzione fastidiosa.

Io voglio , idealmente, di eseguire ciò che è nel file super.sh stupido dal terminale, non il file super.sh: D, senza avere la chiusura terminale su di me. Questo è ciò che accade con ciò che sto cercando di fare:

comando terminale:

my-mac$ source $create_path blah

in create.sh ho ancora set -o errexit

Ecco l'output sul terminale

    sed: 1: "s/@@SITE_NAME@@/blah": unterminated substitute in regular expression
Saving session...
...copying shared history...
...saving history...truncating history files...
...completed.

[Process completed]

E poi il terminale è congelato. Ctrl + C non funziona, né Ctrl + D

Se invece di set -o errexit, se uso semplicemente command || returnistruzioni ovunque nel file create.sh, ottengo esattamente quello che voglio, mentre eseguo le righe in supser.sh direttamente sul terminale (invece di chiamare super.sh dal terminale). Ma questa non è nemmeno una soluzione pratica.

Nota: mi è piaciuta la risposta di @terdon circa la generazione di una shell figlio, quindi ho finito per generare una sub shell tramite lo script anziché il terminale, come ha mostrato nella sua risposta usando le parentesi graffe ( ), attorno all'intero script. La sua risposta funziona anche.


Lo stai facendo in uno script o manualmente?
terdon

Sto chiamando il file con sorgente nel terminale. Come in: source $file_path argumentLo script viene eseguito nella stessa shell da cui l'ho chiamato da (cosa sourcefa, mi è stato detto ... e si comporta in questo modo) le command || returnistruzioni si trovano nel file che sto eseguendo nel terminale

OK, e dove viene eseguito il set? È nello script di provenienza o lo esegui manualmente? Sarebbe molto più semplice rispondere se si potesse aggiungere un semplice esempio che possiamo copiare e provare. Ho un'idea, ma ho bisogno di un modo di test per essere sicuro che funzioni.
terdon

Ho aggiunto una nota al post originale che potrebbe essere d'aiuto. Ma farò anche come mi hai suggerito.

Risposte:


5

Semplicemente fonte il file con un fail-safe:

source the-source-file || true

... quindi il comando generale non fallirà, anche se lo sourcefa.


In realtà, ho pensato che fosse quello che volevo, ma ho scoperto che il mio terminale era solo lento ... In realtà voglio tornare al terminale per errore, (che significa fermare l'intero script nelle sue tracce) ... per qualsiasi motivo, source file || true non lo fa, nemmeno source file || return quando lo

Il sourcing del file non ti restituisce un prompt della shell ??
Jeff Schaller

questo è ciò che è nel mio terminale: Jills-MBP:~ jillr$ source ~/Desktop/site_builder/create.sh blah || trueesegue semplicemente la prossima parte dello script in caso di fallimento, quindi restituisce invece di vero. L'unica cosa che ha funzionato sono le command || returnistruzioni nel file effettivo per tutti i comandi, il che è stupido. Non so se lanciare tutto in una funzione e poi chiamare function || returnalla fine del file sarebbe utile.

Bene, se metti esplicitamente || returnun comando che fallisce, il sì, tornerà. Pensavo che stessimo cercando di impedire l'uscita della shell principale / genitore?
Jeff Schaller

Sì, voglio impedire al terminale di uscire effettivamente. Perché non voglio uscire dal terminal. Voglio uscire dalla sceneggiatura. Il ritorno su singoli comandi nello script esce dallo script e lascia il mio terminale nelle stesse condizioni in cui si preme Ctrl + C per terminare un processo, come l'esecuzione del server dal terminale. Voglio evitare di scrivere il comando || ritorno per ogni riga nel file: D

3

Questa è l'unica cosa che funziona per quello che dovevo realizzare (creare un ambiente virtuale quindi attivarlo, quindi installare i requisiti da uno script bash):

spawn una shell subshell / child dallo script, come in:

stupid_file.sh

(
set -o errexit
#bunch of commands
#one line fails
)

esegui lo stupid_file usando:

source stupid_file.sh <file arguments here> || true

LA FINE.

** si inchina **

(il merito va a Jeff e Terdon)


1
Questo non è molto diverso dal solo eseguire lo script invece di acquistarlo.
Chepner,

@chepner hmmm. Freddo. Dovrò provare a chiamare giusto bash $create_path blahe vedere se esce ancora, si esegue allo stesso modo e installa correttamente le cose nel mio ambiente virtuale. Fuori tempo adesso.

Ti risparmierò il tempo: non lo farà (se per "installa roba" intendi impostare le variabili di ambiente nella tua shell corrente), e nemmeno lo farà (...), poiché tutti i compiti tra parentesi avranno effetto solo su quella subshell. foo=3; (foo=5); echo "$foo"uscirà 3, non 5.
chepner,

@chepner non esattamente. Non riesco a chiamare source filedal terminale e mi aspetto gli stessi risultati. Perché non ha mai funzionato. L'unica volta che ottengo gli stessi risultati è quando creo esplicitamente una sotto shell, sia tramite il terminale, sia dallo script. Posso comunque usare bashinvece di sourceeseguire lo script e ottenere gli stessi risultati, ma solo quando

@chepner per roba di installazione, in realtà intendo creare un ambiente virtuale, attivare quell'ambiente virtuale e quindi eseguire pip install -r $apath/requirements.txtall'interno di quell'ambiente attivato. È per questo che ho usato source in primo luogo per chiamare lo script

1

Come semplice soluzione alternativa, è possibile eseguire una shell nella shell corrente e sorgente lì. Qualcosa di simile a:

  1. Apri un nuovo terminale e imposta tutto nel modo desiderato. Hai citato alcune variabili d'ambiente e simili. Impostali qui.

  2. In quel terminale, avvia una nuova shell. Per esempio, bash.

  3. Fare ciò che sai fare. Fonte la tua sceneggiatura. Se esce, sei appena gettato nel primo guscio e tutto è ancora impostato. Corri di bashnuovo e sei di nuovo in affari.

Per illustrare, ho creato questo script che fallirà se provi a cercarlo:

$ cat /home/terdon/scripts/bar.sh
set -o errexit
var='bar

Vediamo cosa succede se avvio una sessione della shell nidificata e quindi la fonte (nota che sto usando il nome portatile per il sourcecomando .; sourceè un bashismo):

parent-shell $ bash      ## start a new shell
child-shell $ . ~/scripts/bar.sh
bash: /home/terdon/scripts/bar.sh: line 2: unexpected EOF while looking for matching `''
parent-shell $ 

Come puoi vedere, l'errore di sintassi ha causato la chiusura dello script di origine che, a sua volta, ha causato la chiusura della mia sessione di shell ma poiché era una sessione nidificata, mi ha riportato alla shell genitore originale con tutte le variabili ancora impostate . Ora, esegui di nuovo una nuova shell e puoi tornare a cercare il tuo script.


Proverò molto velocemente

Questo ha l'effetto previsto ... il rovescio della medaglia è che le variabili che creo per la shell padre non sono accessibili nella shell figlio, che contiene una delle variabili necessarie per l'esecuzione nella shell figlio. C'è un modo per generare una subshell dal file che sto eseguendo? In questo modo, posso ancora chiamare lo script nella shell padre e posso ancora accedere alle variabili di cui ho bisogno? Sto letteralmente impostando create_path=~/Desktop/site_builder/create.sh nella shell genitore perché lo faccio così spesso. E ne ho bisogno per chiamare source $ create_path [argomento] per eseguire lo script.

1
@JillRussek se le variabili non vengono passate alla shell figlio, non le stai inserendo export. Ma ciò che descrivi ha davvero poco senso. Sembra sempre più un problema XY . Potresti voler pubblicare una nuova domanda che spieghi qual è il tuo obiettivo finale. Scommetto che possiamo darti una soluzione migliore di tutto questo strano approvvigionamento.
terdon

Hmm. Potrei anche provarci. In questo momento, semplicemente generare la shell figlio dallo script anziché dal terminale sta facendo esattamente quello che voglio. Devo estrarre lo script dal terminale per creare un ambiente virtuale nello script e installarlo in esso. Ora funziona come previsto.

Ma hai ragione, non stavo esportando le variabili, perché non sapevo di dover: D. (Non ho mai generato una shell figlio prima)
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.