sourcing d'un script Bash - Retour sur erreur, au lieu de quitter?

17

Je recherche un script bash dans le terminal , donc je quitte en cas d'erreur avec

set -o errexit

tue mon terminal, ce qui est extrêmement ennuyeux, car je dois fermer le terminal, en ouvrir un autre et réinitialiser certaines variables.

Jusqu'à présent, en utilisant

command || return

lignes, dans le script, fait exactement ce que je veux

set -o errexit

à faire ... Mais je veux que ce soit fait pour tout le script; pas seulement une ligne / commande

J'ai un fichier plein de commandes pour configurer un site, et je préfère ne pas faire de commande || revenir

pour chaque ligne du fichier

Y a-t-il une autre option définie, ou autre chose qui "reviendra" au lieu de quitter le terminal?

- Pour plus de clarté , je voudrais tuer le script et laisser le terminal dans le même état que le fait d'appuyer sur ctrl + C pour tuer un service exécuté dans le terminal. command || returnest-ce que. Mais je ne veux pas m'attacher || returnà chaque ligne du fichier. Je cherche donc quelque chose de similaire set -o errexitqui ne provoque pas l'arrêt du terminal

--- Remarque: Création d'un script stupide avec deux lignes (super.sh):

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

Et en plaçant set -o errexiten haut de create.sh,

fonctionne exactement comme je l'attends. Cependant, c'est vraiment stupide de devoir créer un fichier avec deux lignes, juste pour appeler un autre script bash, au lieu de simplement l'appeler depuis le terminal. Ugghhh

voici quelques exemples:

dans super.sh

#!/bin/bash

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

dans 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

dans le terminal:

 $ bash super.sh

sortie comme prévu:

my-mac$

Cela marche. Quelle solution ennuyeuse.

Je souhaite , idéalement, exécuter ce qui se trouve dans le stupide fichier super.sh à partir du terminal, pas le fichier super.sh: D, sans que le terminal ne soit arrêté sur moi. Voici ce qui se passe avec ce que j'essaie de faire:

commande de terminal:

my-mac$ source $create_path blah

dans create.sh j'ai encore set -o errexit

Voici la sortie sur le terminal

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

[Process completed]

Et puis le terminal est gelé. Ctrl + C ne fonctionne pas, pas plus que Ctrl + D

Si au lieu de set -o errexit, si j'utilise simplement des command || returninstructions partout dans le fichier create.sh, alors j'obtiens exactement ce que je veux, tout en exécutant les lignes dans supser.sh directement sur le terminal (au lieu d'appeler super.sh depuis le terminal). Mais ce n'est pas non plus une solution pratique.

Remarque: J'ai aimé la réponse de @terdon à propos de la création d'un shell enfant, donc j'ai fini par générer un sous-shell via le script au lieu du terminal, comme il l'a montré dans sa réponse en utilisant les accolades ( ), autour du script entier. Sa réponse fonctionne aussi.


la source
Faites-vous cela dans un script ou manuellement?
terdon
J'appelle le fichier avec la source dans le terminal. Comme dans: source $file_path argumentLe script est exécuté dans le même shell que je l'ai appelé (qu'est source- ce qui m'a été dit ... et agit comme c'est le cas) les command || returninstructions sont dans le fichier que
OK, et où se déroule l'ensemble? Est-ce dans le script d'origine ou l'exécutez-vous manuellement? Il serait beaucoup plus facile de répondre si vous pouviez ajouter un exemple simple que nous pouvons copier et essayer. J'ai une idée, mais j'ai besoin d'un moyen de tester pour être sûr que ça marche.
terdon
J'ai ajouté une note au message d'origine qui pourrait aider à cela. Mais je ferai aussi ce que vous avez suggéré.

Réponses:

5

Sourcez simplement le fichier avec une sécurité intégrée:

source the-source-file || true

... alors la commande globale n'échouera pas, même si c'est le sourcecas.

Jeff Schaller
la source
En fait, je pensais que c'était ce que je voulais, mais il s'avère que mon terminal était juste lent ... Je veux en fait revenir au terminal en cas d'erreur, (ce qui signifie arrêter tout le script dans ses pistes) ... pour une raison quelconque, source file || true ne fait pas cela, pas plus que source file || return lorsque je tape ceci dans le terminal ...
La recherche du fichier ne vous renvoie pas à une invite du shell ??
Jeff Schaller
voici ce qu'il y a dans mon terminal: Jills-MBP:~ jillr$ source ~/Desktop/site_builder/create.sh blah || trueil exécute simplement la partie suivante du script en cas d'échec, donc retourne au lieu de vrai. La seule chose qui a fonctionné est des command || returninstructions dans le fichier réel pour toutes les commandes, ce qui est stupide. Je ne sais pas si jeter tout cela dans une fonction puis appeler function || returnà la fin du fichier ferait du bien non plus.
Eh bien, si vous mettez explicitement || returnune commande qui échoue, oui, elle reviendra. Je pensais que nous essayions d'empêcher votre shell principal / parent de sortir?
Jeff Schaller
Oui, je souhaite empêcher la sortie du terminal. Parce que je ne veux pas quitter le terminal. Je veux quitter le script. Le retour sur les commandes individuelles du script quitte le script et laisse mon terminal dans la même condition que d'appuyer sur Ctrl + C pour tuer un processus, tel que l'exécution du serveur depuis le terminal. Je veux éviter d'écrire une commande || retour pour chaque ligne du fichier: D
3

C'est la seule chose qui fonctionne pour ce que je devais accomplir (créer un environnement virtuel puis l'activer, puis installer les exigences à partir d'un script bash):

générer un shell sous-shell / enfant à partir du script, comme dans:

stupid_file.sh

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

lancez le stupid_file en utilisant:

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

LA FIN.

** prend un arc **

(crédit à Jeff et Terdon)


la source
1
Ce n'est pas vraiment différent de simplement exécuter le script au lieu de le sourcer.
chepner
@chepner hmmm. Cool. Je vais devoir essayer d'appeler juste bash $create_path blahet voir s'il se ferme toujours, et s'exécute de la même manière, et installe toujours les choses correctement dans mon environnement virtuel. Plus de temps maintenant.
Je vous ferai gagner du temps: ce ne sera pas le cas (si par "installe des trucs", vous voulez définir des variables d'environnement dans votre shell actuel), et ce ne sera pas le cas (...), car toutes les affectations entre parenthèses n'affectent que ce sous-shell. foo=3; (foo=5); echo "$foo"produira 3, pas 5.
chepner
@chepner pas exactement. Je ne peux pas appeler source filedepuis le terminal et attendre les mêmes résultats. Parce que ça n'a jamais marché. La seule fois où j'obtiens les mêmes résultats, c'est quand je crée explicitement un sous-shell, que ce soit via le terminal ou à partir du script. Je peux cependant utiliser bashau lieu de sourcepour exécuter le script, et obtenir les mêmes résultats, mais uniquement lorsque
@chepner par installer des trucs, je veux dire en fait créer un environnement virtuel, activer cet environnement virtuel, puis exécuter pip install -r $apath/requirements.txtdans cet environnement activé. C'est pourquoi j'ai utilisé la source en premier lieu pour appeler le script
1

Comme solution de contournement simple, vous pouvez exécuter un shell dans votre shell actuel et y source. Quelque chose comme:

  1. Ouvrez un nouveau terminal et configurez tout comme vous le souhaitez. Vous avez mentionné certaines variables d'environnement, etc. Installez-les ici.

  2. Dans ce terminal, démarrez un nouveau shell. Par exemple, bash.

  3. Faire votre truc. Sourcez votre script. S'il se termine, vous êtes simplement jeté dans le premier shell et tout est toujours configuré. Courez à bashnouveau et vous êtes de retour aux affaires.

Pour illustrer, j'ai créé ce script qui échouera si vous essayez de le source:

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

Voyons voir ce qui se passe si je commence une session shell imbriquée et la source puis (notez que j'utilise le nom portable pour la sourcecommande, ., sourceest un bash):

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 $ 

Comme vous pouvez le voir, l'erreur de syntaxe a provoqué la fermeture du script d'origine, ce qui a entraîné la fermeture de ma session shell, mais comme il s'agissait d'une session imbriquée, cela m'a simplement renvoyé au shell parent d'origine avec toutes les variables toujours configurées. . Maintenant, exécutez à nouveau un nouveau shell et vous pouvez revenir à la recherche de votre script.

terdon
la source
Je vais essayer ça très rapidement
Cela a l'effet escompté ... l'inconvénient est que les variables que je crée pour le shell parent, ne sont pas accessibles dans le shell enfant, qui contient l'une des variables dont j'ai besoin pour l'exécution dans le shell enfant. Existe-t-il un moyen de générer un sous-shell à partir du fichier que j'exécute? De cette façon, je peux toujours appeler le script dans le shell parent et accéder aux variables dont j'ai besoin? Je me mets littéralement create_path=~/Desktop/site_builder/create.sh dans le shell parent parce que je le fais si souvent. Et j'en ai besoin pour appeler la source $ create_path [argument] pour exécuter le script.
1
@JillRussek si les variables ne sont pas transmises au shell enfant, vous ne exportles utilisez pas . Mais ce que vous décrivez n'a vraiment aucun sens. Cela ressemble de plus en plus à un problème XY . Vous voudrez peut-être publier une nouvelle question expliquant quel est votre objectif final. Je parie que nous pouvons vous donner une meilleure solution que tout ce sourcing bizarre.
terdon
Hmm. Je pourrais aussi essayer ça. À l'heure actuelle, le fait de générer le shell enfant à partir du script plutôt que du terminal fait exactement ce que je veux. Je dois me procurer le script à partir du terminal afin de créer un environnement virtuel dans le script et y installer des trucs. Cela fonctionne comme prévu maintenant.
Mais vous avez raison, je n'exportais pas les variables, car je ne savais pas que je devais: D. (Je n'ai jamais engendré de shell enfant auparavant)