scripts script programas pasar parametros manejo español ejemplos comando cadenas bash shell multiprocessing sh multiprocess

script - cómo escribir un shell bash de proceso



scripts bash ejemplos (9)

Tengo más de 10 tareas para ejecutar, y el sistema restringe que, como máximo, se pueden ejecutar 4 tareas al mismo tiempo.

Mi tarea se puede iniciar como: myprog taskname

¿Cómo puedo escribir un script de shell bash para ejecutar estas tareas. Lo más importante es que cuando una tarea termina, la secuencia de comandos puede comenzar otra inmediatamente, haciendo que las tareas en ejecución cuenten con 4 todo el tiempo.


Este script probado ejecuta 5 trabajos a la vez y reiniciará un nuevo trabajo tan pronto como lo haga (debido a la eliminación del sleep 10.9 cuando obtenemos un SIGCHLD. Una versión más simple de esto podría usar el sondeo directo (cambie el sleep 10.9 a Duerme 1 y deshazte de la trampa).

#!/usr/bin/bash set -o monitor trap "pkill -P $$ -f ''sleep 10/.9'' >&/dev/null" SIGCHLD totaljobs=15 numjobs=5 worktime=10 curjobs=0 declare -A pidlist dojob() { slot=$1 time=$(echo "$RANDOM * 10 / 32768" | bc -l) echo Starting job $slot with args $time sleep $time & pidlist[$slot]=`jobs -p %%` curjobs=$(($curjobs + 1)) totaljobs=$(($totaljobs - 1)) } # start while [ $curjobs -lt $numjobs -a $totaljobs -gt 0 ] do dojob $curjobs done # Poll for jobs to die, restarting while we have them while [ $totaljobs -gt 0 ] do for ((i=0;$i < $curjobs;i++)) do if ! kill -0 ${pidlist[$i]} >&/dev/null then dojob $i break fi done sleep 10.9 >&/dev/null done wait


Me topé con este hilo mientras escribía sobre mi propio grupo de procesos y me gustó especialmente la solución de Brandon Horsley, aunque no pude hacer que las señales funcionaran correctamente, así que me inspiré en Apache y decidí probar un modelo pre-fork con un fifo como mi cola de trabajo

La siguiente función es la función que los procesos de trabajo ejecutan cuando se bifurcan.

# /brief the worker function that is called when we fork off worker processes # /param[in] id the worker ID # /param[in] job_queue the fifo to read jobs from # /param[in] result_log the temporary log file to write exit codes to function _job_pool_worker() { local id=$1 local job_queue=$2 local result_log=$3 local line= exec 7<> ${job_queue} while [[ "${line}" != "${job_pool_end_of_jobs}" && -e "${job_queue}" ]]; do # workers block on the exclusive lock to read the job queue flock --exclusive 7 read line <${job_queue} flock --unlock 7 # the worker should exit if it sees the end-of-job marker or run the # job otherwise and save its exit code to the result log. if [[ "${line}" == "${job_pool_end_of_jobs}" ]]; then # write it one more time for the next sibling so that everyone # will know we are exiting. echo "${line}" >&7 else _job_pool_echo "### _job_pool_worker-${id}: ${line}" # run the job { ${line} ; } # now check the exit code and prepend "ERROR" to the result log entry # which we will use to count errors and then strip out later. local result=$? local status= if [[ "${result}" != "0" ]]; then status=ERROR fi # now write the error to the log, making sure multiple processes # don''t trample over each other. exec 8<> ${result_log} flock --exclusive 8 echo "${status}job_pool: exited ${result}: ${line}" >> ${result_log} flock --unlock 8 exec 8>&- _job_pool_echo "### _job_pool_worker-${id}: exited ${result}: ${line}" fi done exec 7>&- }

Puedes obtener una copia de mi solución en Github. Aquí hay un programa de ejemplo usando mi implementación.

#!/bin/bash . job_pool.sh function foobar() { # do something true } # initialize the job pool to allow 3 parallel jobs and echo commands job_pool_init 3 0 # run jobs job_pool_run sleep 1 job_pool_run sleep 2 job_pool_run sleep 3 job_pool_run foobar job_pool_run foobar job_pool_run /bin/false # wait until all jobs complete before continuing job_pool_wait # more jobs job_pool_run /bin/false job_pool_run sleep 1 job_pool_run sleep 2 job_pool_run foobar # don''t forget to shut down the job pool job_pool_shutdown # check the $job_pool_nerrors for the number of jobs that exited non-zero echo "job_pool_nerrors: ${job_pool_nerrors}"

¡Espero que esto ayude!


Mire mi implementación del grupo de trabajo en bash: https://github.com/spektom/shell-utils/blob/master/jp.sh

Por ejemplo, para ejecutar un máximo de 3 procesos de cURL al descargar desde una gran cantidad de URL, puede ajustar sus comandos de cURL de la siguiente manera:

./jp.sh "My Download Pool" 3 curl http://site1/... ./jp.sh "My Download Pool" 3 curl http://site2/... ./jp.sh "My Download Pool" 3 curl http://site3/... ...


Otra respuesta sobre 4 scripts de shell no me satisface completamente, ya que asume que todas las tareas toman aproximadamente el mismo tiempo y porque requiere una configuración manual. Pero aquí es cómo lo mejoraría.

La secuencia de comandos principal creará enlaces simbólicos a los ejecutables siguiendo cierta convención de namimg. Por ejemplo,

ln -s executable1 ./01-task.01

El primer prefijo es para la clasificación y el sufijo identifica el lote (01-04). Ahora generamos 4 scripts de shell que tomarían el número de lote como entrada y haríamos algo como esto

for t in $(ls ./*-task.$batch | sort ; do t rm t done


Probablemente podrías hacer algo inteligente con señales.

Tenga en cuenta que esto es solo para ilustrar el concepto, y por lo tanto no está probado a fondo.

#!/usr/local/bin/bash this_pid="$$" jobs_running=0 sleep_pid= # Catch alarm signals to adjust the number of running jobs trap ''decrement_jobs'' SIGALRM # When a job finishes, decrement the total and kill the sleep process decrement_jobs() { jobs_running=$(($jobs_running - 1)) if [ -n "${sleep_pid}" ] then kill -s SIGKILL "${sleep_pid}" sleep_pid= fi } # Check to see if the max jobs are running, if so sleep until woken launch_task() { if [ ${jobs_running} -gt 3 ] then ( while true do sleep 999 done ) & sleep_pid=$! wait ${sleep_pid} fi # Launch the requested task, signalling the parent upon completion ( "$@" kill -s SIGALRM "${this_pid}" ) & jobs_running=$((${jobs_running} + 1)) } # Launch all of the tasks, this can be in a loop, etc. launch_task task1 launch_task tast2 ... launch_task task99


Siguiendo la respuesta de @Parag Sardas y la documentación vinculada, aquí hay un script rápido que puede agregar en sus .bash_aliases .

Volver a vincular el enlace del documento porque vale la pena leerlo.

#!/bin/bash # https://.com/a/19618159 # https://.com/a/51861820 # # Example file contents: # touch /tmp/a.txt # touch /tmp/b.txt if [ "$#" -eq 0 ]; then echo "$0 <file> [max-procs=0]" exit 1 fi FILE=${1} MAX_PROCS=${2:-0} cat $FILE | while read line; do printf "%q/n" "$line"; done | xargs --max-procs=$MAX_PROCS -I CMD bash -c CMD

Es decir ./xargs-parallel.sh jobs.txt 4 máximo de 4 procesos leídos de jobs.txt


Sugeriría escribir cuatro guiones, cada uno de los cuales ejecuta un cierto número de tareas en serie. Luego escriba otro script que inicie los cuatro scripts en paralelo. Por ejemplo, si tiene scripts, script1.sh, script2.sh, script3.sh y script4.sh, podría tener un script llamado headscript.sh como tal.

#!/bin/sh ./script1.sh & ./script2.sh & ./script3.sh & ./script4.sh &


Usando GNU Parallel puedes hacer:

cat tasks | parallel -j4 myprog

Si tiene 4 núcleos, incluso puede hacer:

cat tasks | parallel myprog

De http://git.savannah.gnu.org/cgit/parallel.git/tree/README :

Instalación completa

La instalación completa de GNU Parallel es tan simple como:

./configure && make && make install

Instalación personal

Si no eres root, puedes agregar ~ / bin a tu ruta e instalarlo en ~ / bin y ~ / share:

./configure --prefix=$HOME && make && make install

O si su sistema carece de ''make'', simplemente puede copiar src / parallel src / sem src / niceload src / sql a un directorio en su ruta.

Instalación mínima

Si solo necesita paralelo y no tiene instalado ''make'' (tal vez el sistema sea antiguo o Microsoft Windows):

wget http://git.savannah.gnu.org/cgit/parallel.git/plain/src/parallel chmod 755 parallel cp parallel sem mv parallel sem dir-in-your-$PATH/bin/

Probar la instalación

Después de esto deberías poder hacer:

parallel -j0 ping -nc 3 ::: foss.org.my gnu.org freenetproject.org

Esto enviará 3 paquetes de ping a 3 hosts diferentes en paralelo e imprimirá la salida cuando se completen.

Mire el video de introducción para obtener una introducción rápida: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1


Utilice xargs :

xargs -P <maximun-number-of-process-at-a-time> -n <arguments per process> <commnad>

Detalles here .