Llamar a varios scripts de bash y ejecutarlos en paralelo, no en secuencia

19

Supongamos que tengo tres (o más) scripts bash: script1.sh, script2.sh, y script3.sh. Me gustaría llamar a estos tres scripts y ejecutarlos en paralelo . Una forma de hacerlo es simplemente ejecutar los siguientes comandos:

nohup bash script1.sh &
nohup bash script2.sh &
nohup bash script3.sh &

(En general, los scripts pueden tardar varias horas o días en finalizar, por lo que me gustaría usarlos nohuppara que continúen ejecutándose incluso si mi consola se cierra).

Pero, ¿hay alguna forma de ejecutar esos tres comandos en paralelo con una sola llamada?

Estaba pensando algo como

nohup bash script{1..3}.sh &

pero esto parece ejecutarse script1.sh, script2.shy script3.shen secuencia, no en paralelo.

Andrés
fuente
2
¿Qué significa "llamada única"?
jw013
1
¿Cuál es el caso de uso? ¿Tienes un millón de guiones para comenzar?
l0b0
@ jw013 Quiero decir, algo así como un solo comando de línea corta. Si tengo 100 scripts para comenzar, me gustaría poder escribir algo corto (como nohup bash script{1..100}.sh &o for i in {1..100}; do nohup bash script{1..100} &; done), en lugar de escribir nohup bash script*.sh &100 veces diferentes.
Andrew
1
En caso de que los scripts tengan una salida útil: también puede iniciarlos dentro screen(o tmux), para resolver el problema de la consola pero mantener el acceso a la salida (y entrada).
Hauke ​​Laging
1
No hay nada que le impida escribir los 3 comandos en la misma línea. nohup ... & nohup ... & nohup ... &. Si, en cambio, quiere decir que desea ejecutar todos los scripts sin escribir cada nombre de script individualmente, lo hará un bucle simple.
jw013

Respuestas:

16
for((i=1;i<100;i++)); do nohup bash script${i}.sh & done
Hauke ​​Laging
fuente
2
Te estás perdiendo un paréntesis cercano.
David Conrad
1
@HaukeLaging ¿Qué sucede si los nombres de los guiones son diferentes?
Patrick
14

Una mejor manera sería usar GNU Parallel . GNU paralelo es simple y con él podemos controlar la cantidad de trabajos que se ejecutarán en paralelo con más control sobre los trabajos.

En el siguiente comando, script{1..3}.shse expande y se envía como argumentos bashen paralelo. Aquí -j0indica que se deben ejecutar tantos trabajos como sea posible. Por defecto parallelejecuta un trabajo para un núcleo de CPU.

$ parallel -j0 bash :::: <(ls script{1..3}.sh)

Y también puedes intentar usar

$ parallel -j0 bash ::: script{1..3}.sh

Si ejecuta el segundo método si recibe un mensaje de error, significa que esa --tollefopción está configurada /etc/parallel/configy que debe eliminarse y todo funcionará bien.

Puede leer la GNU Parallelspágina de manual aquí para obtener opciones más completas.

Y en caso de que esté ejecutando los trabajos desde una máquina remota, mejor uso screenpara que la sesión no se cierre debido a problemas de red. nohupno es necesario, ya que las versiones recientes de fiesta como viene con huponexittan offy esto evitará que la cáscara de los padres de enviar HUPla señal a sus hijos durante su salida. En caso de que no esté desarmado, hágalo con

$ shopt -u huponexit  
Kannan Mohan
fuente
Si vas a usarlo, bashya que el caparazón parallel -j0 bash :::: <(ls script{1..3}.sh)puede reducirse parallel -j0 bash :::: script{1..3}.sh, ¿no?
iruvar
No, no lo es, cuando '::::' se usa en paralelo significa que el argumento es un archivo que contiene comandos para ejecutar y no el comando en sí. Aquí estamos usando la sustitución de procesos para redirigir los nombres de script dentro de un descriptor de archivos.
Kannan Mohan
Er .. en ese caso, ¿por qué no parallel -j0 bash ::: script{1..3}.sh?
iruvar
Se bash ::: script{1..3}.shestá pasando a parallel, no ::: script{1..3}.sh. Por lo que esta primera debería ampliar a parallel bash ::: script1.sh script2.sh script3.shla concha y luego parallelinvocaciones de bash script1.sh, bash script2.sh, bash script3.sh. Lo intenté
iruvar
1
No, no estoy confundido, todo lo que estoy diciendo es que el problema del OP se resuelve mejor parallel -j0 bash ::: script{1..3}.sh; esto es mejor que el ::::enfoque, ya que evita la necesidad de sustitución del proceso. También el análisis de la salida de ls está plagado de dificultades
iruvar
9

También podemos usar xargspara ejecutar múltiples secuencias de comandos en paralelo.

$ ls script{1..5}.sh|xargs -n 1 -P 0 bash

aquí cada script se pasa a bash como argumento por separado. -P 0indica que el número de procesos paralelos puede ser lo máximo posible. También es más seguro que usar bash default job control feature (&).

Yaalie
fuente
5

Una solución de línea única:

$ nohup bash script1.sh & nohup bash script2.sh & nohup bash script3.sh &

Menos graciosamente, solo use un script de envoltura:

$ cat script.sh
#!/usr/bin/env bash
script1.sh &
script2.sh &
script3.sh &
$ nohup script.sh &

O bucle sobre ellos:

for script in dir/*.sh
do
    nohup bash "$script" &
done
l0b0
fuente
2

Si está buscando ahorrar algo de esfuerzo de escritura

eval "nohup bash "script{1..3}.sh" &"

O pensándolo bien, tal vez no

iruvar
fuente
1

Echa un vistazo a esta herramienta: https://github.com/wagoodman/bashful

Digamos que tienes mytasks.yamlcon

tasks:
    - name: A title for some tasks
      parallel-tasks:
        - cmd: ./script1.sh
        - cmd: ./script2.sh -x an-arg
        - cmd: ./script3.sh -y some-other-arg

Y lo ejecutas así:

nohup bashful run mytasks.yaml

Sus scripts se ejecutarán en paralelo con una barra de progreso vertical (+ eta si lo ha ejecutado antes y el tiempo es determinista). Puede ajustar cuántas tareas desea ejecutar en paralelo si comienza a ejecutar más que solo los 3 dados aquí:

config:
    max-parallel-commands: 6
tasks:
    ...

Descargo de responsabilidad: soy el autor.

Wagoodman
fuente
0

Estoy sugiriendo una utilidad mucho más simple que acabo de escribir. Actualmente se llama par, pero pronto cambiará su nombre a parl o pll, aún no lo he decidido.

https://github.com/k-bx/par

API es tan simple como:

par "script1.sh" "script2.sh" "script3.sh"
Kostiantyn Rybnikov
fuente