Ricerca nel sito web

xarg multi-thread con esempi


Se sei nuovo a xargs, o non sai ancora cos'è xargs, leggi prima il nostro xargs per principianti con esempi. Se sei già un po' abituato a xargs e puoi scrivere istruzioni di base della riga di comando di xargs senza guardare il manuale, allora questo articolo ti aiuterà a diventare più avanzato con xargs sulla riga di comando, specialmente rendendolo multi-thread.

In questo tutorial imparerai:

  • Come utilizzare xargs -P (modalità multi-thread) dalla riga di comando in Bash
  • Esempi di utilizzo avanzati con xarg multithread dalla riga di comando in Bash
  • Una comprensione più approfondita di come applicare xargs multi-thread al codice Bash esistente

xarg multi-thread con esempi

Requisiti software e convenzioni utilizzate

Esempio 1: Chiamata di un'altra shell Bash con l'input compilato xargs

Dopo aver imparato xargs, si scoprirà presto che - mentre xargs permette di fare molte cose potenti da solo - la potenza di xargs sembra essere limitata dalla sua incapacità di eseguire più comandi in sequenza.

Ad esempio, supponiamo di avere una directory che ha sottodirectory denominate da 00 a 10 (11 in totale). E, per ognuna di queste sottodirectory, vogliamo attraversarla e verificare se esiste un file chiamato file.txt e, in tal caso, cat (e unire usando >>) il contenuto di questo file in un file total_file.txt nella directory in cui si trovano le directory da 00 a 10. Proviamo a farlo con xargs in vari passaggi:

mkdir 00 01 02 03 04 05 06 07 08 09 10
ls
00  01  02  03  04  05  06  07  08  09  10
echo 'a' > 03/file.txt
echo 'b' > 07/file.txt
echo 'c' > 10/file.txt

Qui creiamo prima 11 directory, da 00 a 10 e poi creiamo 3 file di file.txt di esempio nelle sottodirectory 03, 07 e 10.

find . -maxdepth 2 -type f -name file.txt
./10/file.txt
./07/file.txt
./03/file.txt

Scriviamo quindi un comando find per individuare tutti i file file.txt a partire dalla directory corrente (.) e che fino a un massimo di 1 livello di sottodirectory:

find . -maxdepth 2 -type f -name file.txt | xargs -I{} cat {} > ./total_file.txt
cat total_file.txt
c
b
a

Il -maxdepth 2 indica la directory corrente (1) e tutte le sottodirectory di questa directory (da qui la maxdepth di 2).

Infine, usiamo xargs (con la stringa di sostituzione {} consigliata e preferita come passata all'opzione xargs -I replace string) per convertire il contenuto di qualsiasi file di questo tipo individuato dal comando find in un file nella directory corrente denominata total_file.txt.

Una cosa bella da notare qui è che, anche se si potrebbe pensare a xargs come all'esecuzione successiva di più comandi cat che reindirizzano tutti allo stesso file, si può usare > (output in un nuovo file, creando il file se non esiste ancora e sovrascrivendo qualsiasi file con lo stesso nome già presente) invece di >> (aggiungere a un file, e crea il file se non esiste ancora)!

L'esercizio finora ha soddisfatto i nostri requisiti, ma non corrispondeva esattamente ai requisiti, vale a dire, non attraversa le sottodirectory. Inoltre, non ha utilizzato il reindirizzamento >> come specificato, anche se l'utilizzo di questo in questo caso avrebbe comunque funzionato.

La sfida con l'esecuzione di più comandi (come il comando cd specifico richiesto per cambiare directory/traverse nella sottodirectory) dall'interno di xargs è che 1) sono molto difficili da codificare e 2) potrebbe non essere possibile codificarlo affatto.

Esiste tuttavia un modo diverso e facile da capire per codificarlo e, una volta che sai come farlo, probabilmente lo utilizzerai in abbondanza. Tuffiamoci.

rm total_file.txt

Per prima cosa abbiamo ripulito il nostro output precedente.

ls -d --color=never [0-9][0-9] | xargs -I{} echo 'cd {}; if [ -r ./file.txt ]; then cat file.txt >> ../total_file.txt; fi'
cd 00; if [ -r ./file.txt ]; then cat file.txt >> ../total_file.txt; fi
cd 01; if [ -r ./file.txt ]; then cat file.txt >> ../total_file.txt; fi
cd 02; if [ -r ./file.txt ]; then cat file.txt >> ../total_file.txt; fi
cd 03; if [ -r ./file.txt ]; then cat file.txt >> ../total_file.txt; fi
cd 04; if [ -r ./file.txt ]; then cat file.txt >> ../total_file.txt; fi
cd 05; if [ -r ./file.txt ]; then cat file.txt >> ../total_file.txt; fi
cd 06; if [ -r ./file.txt ]; then cat file.txt >> ../total_file.txt; fi
cd 07; if [ -r ./file.txt ]; then cat file.txt >> ../total_file.txt; fi
cd 08; if [ -r ./file.txt ]; then cat file.txt >> ../total_file.txt; fi
cd 09; if [ -r ./file.txt ]; then cat file.txt >> ../total_file.txt; fi
cd 10; if [ -r ./file.txt ]; then cat file.txt >> ../total_file.txt; fi

Successivamente, abbiamo formulato un comando, questa volta utilizzando ls, che elencherà tutte le directory che corrispondono all'espressione regolare [0-9][0-9] (leggi il nostro articolo Advanced Bash regex with examples per ulteriori informazioni sulle espressioni regolari).

Abbiamo anche usato xargs, ma questa volta (rispetto agli esempi precedenti) con un comando echo che produrrà esattamente ciò che vorremmo fare, anche se richiede più di uno o più comandi. Pensa a questo come a un mini-copione.

Usiamo anche cd {} per passare alle directory elencate dal comando ls -d (solo directory) (che come nota a margine è protetto dalla clausola --color=never che impedisce a qualsiasi codice colore nell'output di ls di distorcere i nostri risultati), e controlliamo se il file file.txt è presente nella sottodirectory usando un if [ -r ... comando. Se esiste, cattiamo il file.txt in .. /total_file.txt. Si noti il .. Poiché il cd {} nel comando ci ha inserito nella sottodirectory!

Lo eseguiamo per vedere come funziona (dopo tutto, viene eseguito solo l'eco; in realtà non accadrà nulla). Il codice generato sembra fantastico. Facciamo un ulteriore passo avanti ora ed eseguiamo effettivamente lo stesso:

ls -d --color=never [0-9][0-9] | xargs -I{} echo 'cd {}; if [ -r ./file.txt ]; then cat file.txt >> ../total_file.txt; fi' | xargs -I{} bash -c "{}"
cat total_file.txt
a
b
c

Ora abbiamo eseguito lo script totale utilizzando un comando specifico (e sempre lo stesso, cioè vi troverete a scrivere | xargs -I{} bash -c "{}" con una certa regolarità), che esegue tutto ciò che è stato generato dall'eco che lo precede: xargs -I{} bash -c "{}". Fondamentalmente si tratta di dire all'interprete Bash di eseguire tutto ciò che gli è stato passato - e questo per qualsiasi codice generato. Molto potente!

Esempio 2: xarg multithread

Qui daremo un'occhiata a due diversi comandi xargs, uno eseguito senza esecuzione parallela (multi-thread), l'altro con. Considera la differenza tra i due esempi seguenti:

time for i in $(seq 1 5); do echo $[$RANDOM % 5 + 1]; done | xargs -I{} echo "sleep {}; echo 'Done! {}'" | xargs -I{} bash -c "{}"
Done! 5
Done! 5
Done! 2
Done! 4
Done! 1

real    0m17.016s
user    0m0.017s
sys 0m0.003s
time for i in $(seq 1 5); do echo $[$RANDOM % 5 + 1]; done | xargs -I{} echo "sleep {}; echo 'Done! {}'" | xargs -P5 -I{} bash -c "{}"
Done! 1
Done! 3
Done! 3
Done! 3
Done! 5

real    0m5.019s
user    0m0.036s
sys 0m0.015s

La differenza tra le due righe di comando effettive è piccola; abbiamo aggiunto solo -P5 nella seconda riga di comando. Il tempo di esecuzione, tuttavia, (misurato dal prefisso del comando time) è significativo. Scopriamo perché (e perché l'output è diverso!).

Nel primo esempio, creiamo un ciclo for che verrà eseguito 5 volte (a causa della sottoshell $ (seq 1 5) che genera numeri da 1 a 5) e in esso riecheggiamo un numero casuale compreso tra 1 e 5. Successivamente, molto in linea con l'ultimo esempio, abbiamo inviato questo output al comando sleep e anche l'output della durata del sonno come parte del comando Done! eco. Infine lo abbiamo inviato per essere eseguito da un comando Bash di subshell, di nuovo in modo simile al nostro ultimo esempio.

L'output del primo comando funziona in questo modo; Eseguire una sospensione, generare il risultato, eseguire la sospensione successiva e così via.

Il secondo comando, tuttavia, cambia completamente questa situazione. Qui abbiamo aggiunto -P5 che fondamentalmente avvia 5 thread paralleli tutti in una volta!

Il modo in cui funziona questo comando è: avviare x thread (come definito dall'opzione -P) ed elaborarli contemporaneamente. Quando un thread è completo, acquisire immediatamente un nuovo input, non attendere che gli altri thread finiscano prima. L'ultima parte di quella descrizione non è applicabile qui (lo sarebbe solo se ci fossero meno thread specificati da -P allora il numero di 'righe' di input dato, o in altre parole sarebbero disponibili meno thread paralleli rispetto al numero di righe di input).

Il risultato è che i thread che terminano per primi, quelli con un breve tempo di sonno casuale, tornano per primi e producono la loro istruzione 'Fatto!'. Anche l'autonomia totale scende da circa 17 secondi a circa 5 secondi esattamente in tempo reale. Bello!

Conclusione

L'uso di xargs è uno dei modi più avanzati e anche uno dei più potenti per codificare in Bash. Ma non si limita solo all'uso di xargs! In questo articolo abbiamo quindi esplorato l'esecuzione parallela multi-thread tramite l'opzione -P per xargs. Abbiamo anche esaminato la possibilità di chiamare le subshell usando $() e infine abbiamo introdotto un metodo per passare istruzioni multi-comando direttamente a xargs usando una chiamata di subshell bash -c.

Potente? Noi pensiamo di sì! Lasciaci i tuoi pensieri.

Articoli correlati: