abastecimiento de una secuencia de comandos Bash: ¿retorno de error, en lugar de salir?

17

Estoy buscando un script bash en la terminal , así que salgo por error con

set -o errexit

mata mi terminal, lo cual es EXTREMADAMENTE molesto, porque tengo que cerrar la terminal, abrir otra y restablecer algunas variables.

Hasta ahora, usando

command || return

líneas, en el guión, está haciendo exactamente lo que quiero

set -o errexit

hacer ... Pero quiero que se haga para todo el guión; no solo una línea / comando

Tengo un archivo lleno de comandos para configurar un sitio, y prefiero no ejecutar comandos || regreso

por cada línea en el archivo

¿Hay otra opción establecida, o algo más que simplemente "regrese" en lugar de salir del terminal?

- Solo por claridad , me gustaría matar el script y dejar el terminal en el mismo estado que presionar ctrl + C para matar un servicio que se ejecuta en el terminal. command || returnhace eso. Pero no quiero agregar || returntodas las líneas del archivo. Así que estoy buscando algo similar set -o errexit, que no provoque que el terminal se apague

--- Nota: Crear un script tonto con dos líneas (super.sh):

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

Y colocando set -o errexiten la parte superior de create.sh,

funciona exactamente como lo espero. Sin embargo, es realmente estúpido tener que crear un archivo con dos líneas, solo para llamar a otro script bash, en lugar de simplemente llamarlo desde la terminal. Ughhhh

Aquí hay algunos ejemplos:

en super.sh

#!/bin/bash

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

en 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

en la terminal:

 $ bash super.sh

salida como se esperaba:

my-mac$

Esto funciona. Qué solución tan molesta.

Yo quiero , a ser posible, para ejecutar lo que hay en el archivo super.sh estúpida de la terminal, no el archivo super.sh: D, sin tener cerrada la terminal de abajo en mí. Esto es lo que sucede con lo que intento hacer:

comando terminal:

my-mac$ source $create_path blah

en create.sh todavía tengo set -o errexit

Aquí está la salida en la 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]

Y luego la terminal se congela. Ctrl + C no funciona, tampoco Ctrl + D

Si en lugar de set -o errexit, si solo uso command || returndeclaraciones en todas partes en el archivo create.sh, obtengo exactamente lo que quiero, mientras ejecuto las líneas en supser.sh directamente en el terminal (en lugar de llamar a super.sh desde el terminal). Pero esa tampoco es una solución práctica.

Nota: Me gustó la respuesta de @terdon sobre solo generar un shell secundario, así que terminé generando un sub shell a través del script en lugar del terminal, como lo mostró en su respuesta usando los corchetes ( ), alrededor del script completo. Su respuesta también funciona


fuente
¿Estás haciendo esto en un script o manualmente?
terdon
Estoy llamando al archivo con fuente en la terminal. Como en: source $file_path argumentEl script se está ejecutando en el mismo shell desde el que lo llamé (qué source, me han dicho ... y actúa como si fuera el caso), las command || returndeclaraciones están en el archivo que estoy ejecutando en el terminal
OK, ¿y dónde se ejecuta el set? ¿Está en el script de origen o lo ejecutas manualmente? Esto sería mucho más fácil de responder si pudiera agregar un ejemplo simple que podemos copiar y probar. Tengo una idea, pero necesito una forma de prueba para asegurarme de que funciona.
terdon
Agregué una Nota a la publicación original que podría ayudar con eso. Pero también haré lo que me sugirió.

Respuestas:

5

Simplemente obtenga el archivo con una prueba de fallos:

source the-source-file || true

... entonces el comando general no fallará, incluso si lo sourcehace.

Jeff Schaller
fuente
En realidad, pensé que esto era lo que quería, pero resulta que mi terminal estaba siendo lenta ... Realmente quiero volver a la terminal por error, (es decir, detener todo el script en sus pistas) ... por cualquier razón, source file || true no hace esto, tampoco lo hace source file || return cuando
¿Obtener el archivo no te devuelve a un indicador de shell?
Jeff Schaller
Esto es lo que hay en mi terminal: Jills-MBP:~ jillr$ source ~/Desktop/site_builder/create.sh blah || truesimplemente ejecuta la siguiente parte del script en caso de error, por lo que regresa en lugar de verdadero. Lo único que ha funcionado son las command || returndeclaraciones en el archivo real para todos los comandos, lo cual es una tontería. No sé si incluirlo todo en una función y luego llamar function || returnal final del archivo tampoco sería bueno.
Bueno, si explícitamente pones || returnun comando que falla, el sí, volverá. ¿Pensé que estábamos tratando de evitar que su shell principal / principal salga?
Jeff Schaller
Sí, quiero evitar que el terminal salga realmente. Porque no quiero salir de la terminal. Quiero salir del guión. El retorno de comandos individuales en el script sale del script y deja mi terminal en la misma condición que presionar Ctrl + C para matar un proceso, como ejecutar el servidor desde el terminal. Quiero evitar escribir el comando || volver para cada línea en el archivo: D
3

Esto es lo único que funciona para lo que necesitaba lograr (crear un entorno virtual y luego activarlo, luego instalar los requisitos de un script bash):

generar un shell subshell / hijo del script, como en:

stupid_file.sh

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

ejecuta el archivo stupid_file usando:

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

EL FIN.

** hace una reverencia **

(el crédito va para Jeff y Terdon)


fuente
1
Esto no es realmente diferente de simplemente ejecutar el script en lugar de obtenerlo.
chepner
@chepner hmmm. Frio. Tendré que intentar llamar solo bash $create_path blahpara ver si aún sale, y se ejecuta de la misma manera, y aún instala cosas en mi entorno virtual correctamente. Fuera de tiempo ahora.
Le ahorraré tiempo: no lo hará (si por "instala cosas" quiere decir establecer variables de entorno en su shell actual), y tampoco lo hará (...), ya que todas las asignaciones entre paréntesis solo afectan esa subshell. foo=3; (foo=5); echo "$foo"es la salida 3, 5. No
chepner
@chepner no exactamente. No puedo llamar source filedesde la terminal y esperar los mismos resultados. Porque eso nunca funcionó. La única vez que obtengo los mismos resultados es cuando creo un sub shell explícitamente, ya sea a través del terminal o desde el script. Sin embargo, puedo usar en bashlugar de sourceejecutar el script y obtener los mismos resultados, pero solo cuando ejecuto mi script en un sub shell explícitamente
@chepner por instalar cosas, me refiero a crear un entorno virtual, activar ese entorno virtual y luego ejecutarlo pip install -r $apath/requirements.txtdentro de ese entorno activado. Es por eso que usé source en primer lugar para llamar al script
1

Como una solución alternativa simple, puede ejecutar un shell en su shell actual y fuente allí. Algo como:

  1. Abra una nueva terminal y configure todo como lo desee. Usted mencionó algunas variables de entorno y similares. Ponlos aquí.

  2. En esa terminal, inicie un nuevo shell. Por ejemplo, bash.

  3. Haz tus cosas. Busca tu guión. Si sale, acaba de ser arrojado al primer shell y todo está configurado. Solo corre de bashnuevo y estarás de vuelta en el negocio.

Para ilustrar, creé este script que fallará si intentas obtenerlo:

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

Veamos lo que sucede si comienzo a una sesión de shell anidada y luego la fuente él (nota que estoy usando el nombre portátil para el sourcecomando, .; sourcees una del 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 $ 

Como puede ver, el error de sintaxis hizo que la secuencia de comandos de origen saliera, lo que a su vez hizo que mi sesión de shell se cerrara, pero dado que era una sesión anidada, eso me devolvió al shell principal original con todas las variables aún configuradas . Ahora, simplemente ejecute un nuevo shell nuevamente y puede volver a obtener su script.

terdon
fuente
Voy a probar esto realmente rápido
Esto tiene el efecto deseado ... la desventaja es que las variables que creo para el shell principal no son accesibles en el shell secundario, que contiene una de las variables que necesito para la ejecución en el shell secundario. ¿Hay alguna manera de generar una subshell del archivo que estoy ejecutando? De esa manera, ¿todavía puedo llamar al script en el shell principal y aún puedo acceder a las variables que necesito? Literalmente estoy configurando create_path=~/Desktop/site_builder/create.sh en el shell principal porque lo hago muy a menudo. Y necesito que llame a source $ create_path [argumento] para ejecutar el script.
1
@JillRussek si las variables no se pasan al shell secundario, no las está exportutilizando. Pero lo que describe realmente tiene muy poco sentido. Suena cada vez más como un problema XY . Es posible que desee publicar una nueva pregunta que explique cuál es su objetivo final. Apuesto a que podemos darle una mejor solución que todas estas fuentes extrañas.
terdon
Hmm Yo también podría darle una oportunidad. En este momento, solo generar el shell hijo del script en lugar del terminal está haciendo exactamente lo que quiero que haga. Tengo que obtener el script desde el terminal para crear un entorno virtual en el script, y pip instalar cosas en él. Está funcionando según lo previsto ahora.
Pero tienes razón, no estaba exportando las variables, porque no sabía que necesitaba: D. (Nunca he engendrado una cáscara niño antes)