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.