Chiamare le funzioni della shell con xargs


168

Sto cercando di usare xargs per chiamare una funzione più complessa in parallelo.

#!/bin/bash
echo_var(){
    echo $1
    return 0
}
seq -f "n%04g" 1 100 |xargs -n 1 -P 10 -i echo_var {} 
exit 0

Questo restituisce l'errore

xargs: echo_var: No such file or directory

Qualsiasi idea su come posso usare xargs per ottenere questo risultato o qualsiasi altra soluzione sarebbe gradita.


2
Pericolo, utente 1148366, pericolo! Non usare bash per la programmazione parallela: incontrerai così tanti problemi. Usa C / C ++ e pthreads, o thread Java o qualsiasi cosa ti faccia riflettere a lungo su ciò che stai facendo, perché la programmazione parallela richiede molto pensiero per ottenere il risultato giusto.
David Souther,

27
@DavidSouther Se le attività sono indipendenti, come convertire tutti questi file di immagini in png, non preoccuparti. È quando hai la sincronizzazione (oltre l'attesa che tutto finisca) e la comunicazione che diventa disordinata.
ctrl-alt-delor,

@DavidSouther - Sono un appassionato di Java da molto tempo e ho lavorato molto tardi. E continuo a dire alla gente: gli amici non lasciano che gli amici scrivano script bash. Eppure, mi ritrovo a guardare questo post / soluzione perché (faccia triste :() Sono impegnato in elaborazione parallela in bash. Potrei prontamente farlo in groovy / java. Cattivo!
Christian Bongiorno

Risposte:


172

L'esportazione della funzione dovrebbe farlo (non testato):

export -f echo_var
seq -f "n%04g" 1 100 | xargs -n 1 -P 10 -I {} bash -c 'echo_var "$@"' _ {}

È possibile utilizzare il comando incorporato printfanziché quello esterno seq:

printf "n%04g\n" {1..100} | xargs -n 1 -P 10 -I {} bash -c 'echo_var "$@"' _ {}

Inoltre, usando return 0e exit 0così si maschera qualsiasi valore di errore che potrebbe essere prodotto dal comando che lo precede. Inoltre, se non ci sono errori, è l'impostazione predefinita e quindi in qualche modo ridondante.

@phobic menziona che il comando Bash potrebbe essere semplificato

bash -c 'echo_var "{}"'

spostando {}direttamente al suo interno. Ma è vulnerabile al comando di iniezione come sottolineato da @Sasha.

Ecco un esempio per cui non dovresti usare il formato incorporato:

$ echo '$(date)' | xargs -I {} bash -c 'echo_var "{}"'
Sun Aug 18 11:56:45 CDT 2019

Un altro esempio del perché no :

echo '\"; date\"' | xargs -I {} bash -c 'echo_var "{}"'

Questo è ciò che viene emesso usando il formato sicuro :

$ echo '$(date)' | xargs -I {} bash -c 'echo_var "$@"' _ {}
$(date)

Questo è paragonabile all'utilizzo di query SQL con parametri per evitare l' iniezione .

Sto usando dateuna sostituzione di comando o tra virgolette di escape qui invece del rmcomando usato nel commento di Sasha poiché non è distruttivo.


14
Un po 'più di discussione: xargs esegue un'istanza completamente nuova del processo denominato. In questo caso, si fornisce il nome echo_var, che è una funzione in questo script, non un processo (programma) nel PERCORSO. Quello che fa la soluzione di Dennis è esportare la funzione che i processi bash figlio devono usare, quindi eseguire il fork al sottoprocesso ed eseguirla lì.
David Souther,

7
qual è il significato di _e \, senza di loro, non funzionava per me
Hashbrown,

9
@Hashbrown: il carattere di sottolineatura ( _) fornisce un segnaposto per argv[0]( $0) e quasi tutto può essere utilizzato lì. Penso di aver aggiunto il backslash-punto e virgola ( \;) a causa del suo utilizzo nel terminare la -execclausola find, ma funziona senza di me qui. In effetti, se la funzione dovesse essere utilizzata $@invece di $1quella, vedrebbe il punto e virgola come parametro, quindi dovrebbe essere omesso.
In pausa fino a nuovo avviso.

4
-i argomento per xargs è stato deprecato. Utilizzare invece -I (maiuscolo i).
Nicolai S

11
Puoi semplificarlo includendo l'argomento di xargs nella stringa di comando per bash con bash -c 'echo_var "{}"'. Quindi non è necessario il _ {} alla fine.
fobico

16

L'uso di GNU Parallel è simile al seguente:

#!/bin/bash
echo_var(){
    echo $1
    return 0
}
export -f echo_var
seq -f "n%04g" 1 100 | parallel -P 10 echo_var {} 
exit 0

Se si utilizza la versione 20170822, non è necessario fino export -fa quando si esegue questo:

. `which env_parallel.bash`
seq -f "n%04g" 1 100 | env_parallel -P 10 echo_var {} 

dove trovo shopt per osx?
Nick,

nvm è impostato in zsh
Nick,

Ottenere questo sotto eerror Ole sh: parallel_bash_environment: line 67: unexpected EOF while looking for matching '' sh: parallel_bash_environment: linea 79: errore di sintassi: fine imprevista del file sh: errore durante l'importazione della definizione della funzione per parallel_bash_environment' /usr/local/bin/bash: parallel_bash_environment: line 67: unexpected EOF while looking for matching '' / usr / local / bin / bash: parallel_bash_environment: linea 79: errore di sintassi: fine imprevista di file / usr / local / bin / bash: errore durante l'importazione della definizione della funzione per `...
Nick,

Sei stato shellaftershocked: Shellshock non ha influenzato direttamente GNU Parallel. La soluzione allo shellshock, tuttavia, ha funzionato: ha completamente rotto --env e il trucco env_parallel. Si ritiene che sia stato corretto nella versione git: git.savannah.gnu.org/cgit/parallel.git/snapshot/…
Ole Tange,

1
Mi piace questa risposta, perché mi ha fatto scoprire lo strumento parallelo
JR Utily,

10

Qualcosa del genere dovrebbe funzionare anche:

function testing() { sleep $1 ; }
echo {1..10} | xargs -n 1 | xargs -I@ -P4 bash -c "$(declare -f testing) ; testing @ ; echo @ "

1

Forse questa è una cattiva pratica, ma se stai definendo funzioni in uno .bashrco altri script, puoi avvolgere il file o almeno le definizioni delle funzioni con un'impostazione di allexport:

set -o allexport

function funcy_town {
  echo 'this is a function'
}
function func_rock {
  echo 'this is a function, but different'
}
function cyber_func {
  echo 'this function does important things'
}
function the_man_from_funcle {
  echo 'not gonna lie'
}
function funcle_wiggly {
  echo 'at this point I\'m doing it for the funny names'
}
function extreme_function {
  echo 'goodbye'
}

set +o allexport
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.