bash - script - run command in background linux
bash: inicia múltiples comandos encadenados en el fondo (14)
Estoy intentando ejecutar algunos comandos en paralelo, en segundo plano, usando bash. Esto es lo que trato de hacer:
forloop {
//this part is actually written in perl
//call command sequence
print `touch .file1.lock; cp bigfile1 /destination; rm .file1.lock;`;
}
La parte entre los palos de atrás (``) genera un nuevo shell y ejecuta los comandos en sucesión. El problema es que el control del programa original vuelve solo después de que se haya ejecutado el último comando. Me gustaría ejecutar toda la instrucción en segundo plano (no espero ningún valor de salida / retorno) y me gustaría que el ciclo continúe ejecutándose.
El programa que realiza la llamada (el que tiene el ciclo) no finalizará hasta que finalicen todos los shells generados.
Podría usar hilos en perl para engendrar diferentes hilos que llaman diferentes caparazones, pero parece una exageración ...
¿Puedo iniciar un shell, darle un conjunto de comandos y decirle que vaya al fondo?
Gracias Hugh, eso lo hizo:
adrianp@frost:~$ (echo "started"; sleep 15; echo "stopped")
started
stopped
adrianp@frost:~$ (echo "started"; sleep 15; echo "stopped") &
started
[1] 7101
adrianp@frost:~$ stopped
[1]+ Done ( echo "started"; sleep 15; echo "stopped" )
adrianp@frost:~$
Las otras ideas no funcionan porque comienzan cada comando en segundo plano, y no la secuencia de comandos (¡lo cual es importante en mi caso!).
¡Gracias de nuevo!
Intenta poner los comandos en llaves con & s, así:
{command1 & ; command2 & ; command3 & ; }
Esto no crea un subconjunto, sino que ejecuta el grupo de comandos en el fondo.
HTH
No he probado esto, pero ¿qué tal
print `(touch .file1.lock; cp bigfile1 /destination; rm .file1.lock;) &`;
Los paréntesis significan ejecutar en una subcapa pero eso no debería doler.
No sé por qué nadie respondió con la solución adecuada:
my @children;
for (...) {
...
my $child = fork;
exec "touch .file1.lock; cp bigfile1 /destination; rm .file1.lock;" if $child == 0;
push @children, $child;
}
# and if you want to wait for them to finish,
waitpid($_) for @children;
Esto hace que Perl genere que los niños ejecuten cada comando, y le permite esperar a que todos los niños completen antes de continuar.
Por cierto,
print `some command`
y
system "some command"
muestra el mismo contenido en stdout, pero el primero tiene una sobrecarga superior, ya que Perl tiene que capturar todo el resultado de " some command
"
Ejecute el comando usando un trabajo at:
# date
# jue sep 13 12:43:21 CEST 2012
# at 12:45
warning: commands will be executed using /bin/sh
at> command1
at> command2
at> ...
at> CTRL-d
at> <EOT>
job 20 at Thu Sep 13 12:45:00 2012
El resultado será enviado a su cuenta por correo.
La instalación en bash que está buscando se llama Compound Commands
. Vea la página man para más información:
Comandos compuestos Un comando compuesto es uno de los siguientes:
(list) list is executed in a subshell environment (see COMMAND EXECUTION ENVIRONMENT below). Variable assignments and builtin commands that affect the shell''s environment do not remain in effect after the command completes. The return status is the exit status of list. { list; } list is simply executed in the current shell environment. list must be terminated with a newline or semicolon. This is known as a group command. The return status is the exit status of list. Note that unlike the metacharac‐ ters ( and ), { and } are reserved words and must occur where a reserved word is permitted to be recognized. Since they do not cause a word break, they must be separated from list by whitespace or another shell metacharac‐ ter.
Hay otros, pero estos son probablemente los 2 tipos más comunes. El primero, los parens, ejecutará una lista de comandos en serie en una subcadena, mientras que el segundo, las llaves, mostrará una lista de comandos en serie en el shell actual.
Parens
% ( date; sleep 5; date; )
Sat Jan 26 06:52:46 EST 2013
Sat Jan 26 06:52:51 EST 2013
llaves
% { date; sleep 5; date; }
Sat Jan 26 06:52:13 EST 2013
Sat Jan 26 06:52:18 EST 2013
Otra forma es usar sintaxis:
{ command1; command2; command3; } &
wait
No hay ningún comando & after pero uno al final del grupo cmd. La wait
al final asegura que el hijo generado tendrá padre hasta que termine.
También puede hacer cosas sofisticadas como redirigir stderr
y stdout
:
{ command1; command2; command3; } 2>&2 1>&1 &
Su ejemplo se vería así:
forloop() {
{ touch .file1.lock; cp bigfile1 /destination; rm .file1.lock; } &
}
# ... do some other concurrent stuff
wait # wait for childs to end
En caso de que alguien todavía esté interesado, puede hacerlo sin llamar a una subshell como esta:
print `touch .file1.lock && cp bigfile1 /destination && rm .file1.lock &`;
Bifurcar en un bucle for:
for i in x; do ((a; b; c;)&); done
Ejemplo:
for i in 500 300 100; do ((printf "Start $i: "; date; dd if=/dev/zero of=testfile_$i bs=1m count=$i 2>/dev/null; printf "End $i: "; date;)&) && sleep 1; done
Puede usar el comando parallel
GNU para ejecutar trabajos en paralelo. Es más seguro es más rápido.
Supongo que está intentando copiar múltiples archivos grandes desde el origen hasta el destino. Y para eso puedes hacer eso en paralelo con la declaración siguiente.
$ ls *|parallel -kj0 --eta ''cp {} /tmp/destination''
Como hemos usado la opción -j0
, todos los archivos se copiarán en paralelo. En caso de que necesite reducir el número de procesos paralelos, puede usar -j<n>
donde <n>
es el número de procesos paralelos que se ejecutarán.
Parallel también recopilará el resultado del proceso y lo informará de forma secuencial (con la opción -k
) que otro mecanismo de control del trabajo no puede hacer.
--eta
opción --eta
le dará una estadística detallada del proceso que está sucediendo. Entonces podemos saber cómo se ha completado el proceso y cuánto tiempo llevará terminarlo.
for command in $commands
do
"$command" &
done
wait
El símbolo comercial al final del comando lo ejecuta en segundo plano, y la wait
espera hasta que se completa la tarea de fondo.
Me encontré con este hilo aquí y decidí armar un fragmento de código para generar declaraciones encadenadas como trabajos de fondo. Probé esto en BASH para Linux, KSH para IBM AIX y Busybox''s ASH para Android, así que creo que es seguro decir que funciona en cualquier shell tipo Bourne.
processes=0;
for X in `seq 0 10`; do
let processes+=1;
{ { echo Job $processes; sleep 3; echo End of job $processes; } & };
if [[ $processes -eq 5 ]]; then
wait;
processes=0;
fi;
done;
Este código ejecuta una cantidad de trabajos en segundo plano hasta un cierto límite de trabajos simultáneos. Puede usar esto, por ejemplo, para recomprimir una gran cantidad de archivos comprimidos con xz
sin tener un gran grupo de procesos xz
comer su memoria completa y hacer que su computadora vomite: en este caso, usa *
como la lista de ''y'' el trabajo por lotes sería gzip -cd "$X" | xz -9c > "${X%.gz}.xz"
gzip -cd "$X" | xz -9c > "${X%.gz}.xz"
.
ejecuta los comandos en una subshell:
(command1 ; command2 ; command3) &
GavinCattell obtuvo el más cercano (por bash, IMO), pero como Mad_Ady señaló, no manejaría los archivos de "bloqueo". Esto debería:
Si hay otros trabajos pendientes, la espera también los esperará. Si necesita esperar solo las copias, puede acumular esos PID y esperar solo esos. Si no, puedes eliminar las 3 líneas con "pids", pero es más general.
Además, agregué una verificación para evitar la copia por completo:
pids=
for file in bigfile*
do
# Skip if file is not newer...
targ=/destination/$(basename "${file}")
[ "$targ" -nt "$file" ] && continue
# Use a lock file: ".fileN.lock" for each "bigfileN"
lock=".${file##*/big}.lock"
( touch $lock; cp "$file" "$targ"; rm $lock ) &
pids="$pids $!"
done
wait $pids
Por cierto, parece que está copiando nuevos archivos a un repositorio FTP (o similar). De ser así, podría considerar una estrategia de copia / cambio de nombre en lugar de los archivos de bloqueo (pero ese es otro tema).