Domanda

Qual è il modo migliore / più semplice per creare un sistema di coda di attività minimo per Linux usando bash e strumenti comuni?

Ho un file con 9'000 righe, ogni riga ha una riga di comando bash, i comandi sono completamente indipendenti.

command 1 > Logs/1.log
command 2 > Logs/2.log
command 3 > Logs/3.log
...

La mia scatola ha più di un core e voglio eseguire X task contemporaneamente. Ho cercato sul web un buon modo per farlo. Apparentemente, molte persone hanno questo problema, ma finora nessuno ha una buona soluzione.

Sarebbe bello se la soluzione avesse le seguenti caratteristiche:

  • può interpretare più di un comando (ad es. comando; comando )
  • può interpretare i reindirizzamenti di stream sulle linee (ad es. ls > /tmp/ls.txt )
  • utilizza solo strumenti Linux comuni

Punti bonus se funziona su altri cloni Unix senza requisiti troppo esotici.

È stato utile?

Soluzione

Puoi convertire la tua lista di comandi in un Makefile? In tal caso, puoi semplicemente eseguire " make -j X " ;.

Altri suggerimenti

GNU Parallel http://www.gnu.org/software/parallel/ è uno strumento più generale per la parallelizzazione rispetto a PPSS.

Se il file di esecuzione contiene:

command 1 > Logs/1.log
command 2 > Logs/2.log
command 3 > Logs/3.log

puoi fare:

cat runfile | parallel -j+0

che eseguirà un comando per core della CPU.

Se i tuoi comandi sono semplici come sopra non hai nemmeno bisogno di runfile ma puoi farlo:

seq 1 3 | parallel -j+0 'command {} > Logs/{}.log'

Se hai più computer disponibili per eseguire l'elaborazione, potresti voler esaminare le opzioni --sshlogin e --trc per GNU Parallel.

Ok, dopo aver pubblicato la domanda qui, ho trovato il seguente progetto che sembra promettente: ppss .

Modifica: non proprio quello che voglio, PPSS si concentra sull'elaborazione di "tutti i file nella directory A".

Bene, questa è comunque una specie di domanda divertente.

Ecco cosa farei, supponendo bash (1) ovviamente.

  • capisce quanti di questi comandi possono essere utilmente eseguiti contemporaneamente. Non sarà solo il numero di core; molti comandi saranno sospesi per l'I / O e quel genere di cose. Chiama quel numero N. N = 15 per esempio.
  • imposta un gestore del segnale trap per il segnale SIGCHLD, che si verifica quando termina un processo figlio. trap signalHandler SIGCHLD
  • cat il tuo elenco di comandi in una pipe
  • scrive un ciclo che legge stdin ed esegue i comandi uno per uno, diminuendo un contatore. Quando il contatore è 0, aspetta s.
  • il tuo gestore di segnale, che gira su SIGCHLD, incrementi quel contatore.

Quindi ora esegue i primi comandi N , quindi attende. Quando il primo figlio termina, l'attesa ritorna, legge un'altra riga, esegue un nuovo comando e attende di nuovo.

Ora, questo è un caso che si occupa di molti lavori che terminano vicini. sospetto che tu possa cavartela con una versione più semplice:

 N=15
 COUNT=N
 cat mycommands.sh | 
 while read cmd 
 do
   eval $cmd &
   if $((count-- == 0))
   then
       wait
   fi
 od

Ora, questo avvierà i primi 15 comandi, quindi eseguirà il resto uno alla volta al termine di alcuni comandi.

Un simile divertimento di calcolo distribuito è lo script Mapreduce Bash:

http://blog.last.fm/2009/04 / 06 / MapReduce-bash-script

E grazie per aver sottolineato i pps!

Puoi usare il comando xargs , il suo --max-procs fa quello che vuoi. Ad esempio, la soluzione di Charlie Martin diventa con xargs:

tr '\012' '\000' <mycommands.sh |xargs --null --max-procs=$X bash -c

dettagli:

  • X è il numero di processi max. Ad esempio: X = 15. --max-procs sta facendo la magia
  • la prima tr è qui per terminare le righe con byte nulli per xargs - opzione nulla in modo che il reindirizzamento delle virgolette ecc. non venga espanso in modo errato
  • bash -c esegue il comando

L'ho provato con questo file mycommands.sh per esempio:

date
date "+%Y-%m-%d" >"The Date".txt
wc -c <'The Date'.txt >'The Count'.txt

Questo è un caso specifico, ma se si sta tentando di elaborare un set di file e produrre un altro set di file di output, è possibile avviare #cores numero di processi e verificare se esiste un file di output prima di elaborarlo. L'esempio seguente converte una directory di file .m4b in file .mp3:

Esegui questo comando tutte le volte che hai i core:

ls * m4b | mentre leggi f; fai il test -f $ {f% m4b} mp3 || mencoder -of rawaudio " $ f " -oac mp3lame -ovc copy -o $ {f% m4b} mp3; fatto & amp;

Potresti vedere la mia coda di compiti scritta su bash: https://github.com/pavelpat/yastq

Coda attività + Parallelizzata + Aggiunta dinamica

Usando un FIFO, questo script si fork per elaborare la coda. In questo modo, puoi aggiungere comandi alla coda al volo (quando la coda è già avviata).

Utilizzo: ./queue Command [# of children] [Nome coda]

Esempio, con 1 thread:

./queue "sleep 5; echo ONE"
./queue "echo TWO"

Output:

ONE
TWO

Esempio, con 2 thread:

./queue "sleep 5; echo ONE" 2
./queue "echo TWO"

Output:

TWO
ONE

Esempio, con 2 code:

./queue "sleep 5; echo ONE queue1" 1 queue1
./queue "sleep 3; echo ONE queue2" 1 queue2

Output:

ONE queue2
ONE queue1

Lo script (salvalo come " coda " e chmod + x coda):

    #!/bin/bash

    #Print usage
    [[ $# -eq 0 ]] && echo Usage: <*> Command [# of children] [Queue name] && exit

    #Param 1 - Command to execute
    COMMAND="$1"

    #Param 2 - Number of childs in parallel
    MAXCHILD=1
    [[ $# -gt 1 ]] && MAXCHILD="$2"

    #Param 3 - File to be used as FIFO
    FIFO="/tmp/defaultqueue"
    [[ $# -gt 2 ]] && FIFO="$3"

    #Number of seconds to keep the runner active when unused
    TIMEOUT=5

    runner(){
      #Associate file descriptor 3 to the FIFO
      exec 3"$FIFO"

      while read -u 3 -t $TIMEOUT line; do
        #max child check
        while [ `jobs | grep Running | wc -l` -ge "$MAXCHILD" ]; do
          sleep 1
        done

        #exec in backgroud
        (eval "$line")&
      done
      rm $FIFO
    }

    writer(){
      #fork if the runner is not running
      lsof $FIFO >/dev/null || (<*> "QueueRunner" "$MAXCHILD" "$FIFO" &)

      #send the command to the runner
      echo "$COMMAND" > $FIFO
    }

    #Create the FIFO file
    [[ -e "$FIFO" ]] || mkfifo "$FIFO"

    #Start the runner if in the runner fork, else put the command in the queue
    [[ "$COMMAND" == "QueueRunner" ]] && runner || writer

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top