Fungsi shell panggilan dengan xargs

168

Saya mencoba menggunakan xargs untuk memanggil fungsi yang lebih kompleks secara paralel.

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

Ini mengembalikan kesalahan

xargs: echo_var: No such file or directory

Setiap ide tentang bagaimana saya dapat menggunakan xargs untuk mencapai hal ini, atau solusi lain akan diterima.

fac3
sumber
2
Bahaya, user1148366, Bahaya! Jangan gunakan bash untuk pemrograman paralel - Anda akan mengalami begitu banyak masalah. Gunakan C / C ++ dan pthreads, atau utas Java, atau apa pun yang membuat Anda berpikir panjang dan keras tentang apa yang Anda lakukan, karena pemrograman paralel membutuhkan banyak pemikiran untuk mendapatkan yang benar.
David Souther
27
@ DavidSouther Jika tugas-tugas independen, seperti mengkonversi semua file gambar ini ke png, maka jangan khawatir. Saat Anda memiliki sinkronisasi (di luar menunggu semua selesai) dan komunikasi itu menjadi berantakan.
ctrl-alt-delor
@ DavidSouther - Saya sudah lama dev Java dan saya telah bekerja di groovy akhir-akhir ini. Dan saya terus memberi tahu orang-orang: Teman-teman jangan biarkan teman menulis skrip bash. Namun, saya menemukan diri saya melihat posting ini / solusi karena (wajah sedih :() Saya terlibat dalam pemrosesan paralel di bash. Saya dapat melakukannya dengan mudah di groovy / java. Buruk!
Christian Bongiorno
Juga dibahas di unix.stackexchange.com/questions/158564/…
Joshua Goldberg

Jawaban:

172

Mengekspor fungsi harus melakukannya (belum diuji):

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

Anda dapat menggunakan builtin printfalih-alih eksternal seq:

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

Juga, menggunakan return 0dan exit 0menyukai itu menutupi nilai kesalahan apa pun yang mungkin dihasilkan oleh perintah sebelumnya. Juga, jika tidak ada kesalahan, itu adalah default dan karenanya agak berlebihan.

@ phobic menyebutkan bahwa perintah Bash dapat disederhanakan

bash -c 'echo_var "{}"'

memindahkan {}langsung di dalamnya. Tapi itu rentan terhadap injeksi perintah seperti yang ditunjukkan oleh @Sasha.

Berikut adalah contoh mengapa Anda tidak boleh menggunakan format yang disematkan:

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

Contoh lain mengapa tidak :

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

Ini adalah keluaran yang menggunakan format aman :

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

Ini sebanding dengan menggunakan query SQL parameter untuk menghindari injeksi .

Saya menggunakan datesubstitusi perintah atau dalam tanda kutip di sini alih-alih rmperintah yang digunakan dalam komentar Sasha karena itu tidak merusak.

Dijeda sampai pemberitahuan lebih lanjut.
sumber
14
Diskusi lebih lanjut: xargs mengeksekusi instance baru dari proses bernama. Dalam hal ini, Anda memberikan nama echo_var, yang merupakan fungsi dalam skrip ini, bukan proses (program) di PATH Anda. Apa yang dilakukan solusi Dennis adalah mengekspor fungsi untuk proses bash anak untuk digunakan, kemudian bercabang ke subproses dan dijalankan di sana.
David Souther
7
apa pentingnya _dan \, tanpa mereka itu tidak bekerja untuk saya
Hashbrown
9
@ Hashbrown: Garis bawah ( _) menyediakan tempat penampung untuk argv[0]( $0) dan hampir semua hal bisa digunakan di sana. Saya rasa saya menambahkan backslash-semicolon ( \;) karena penggunaannya dalam mengakhiri -execklausa find, tetapi berfungsi untuk saya tanpa itu di sini. Bahkan, jika fungsi itu digunakan $@alih-alih $1maka itu akan melihat titik koma sebagai parameter, sehingga harus dihilangkan.
Dijeda sampai pemberitahuan lebih lanjut.
4
Argumen -i ke xargs sudah tidak digunakan lagi. Gunakan -I (modal i) sebagai gantinya.
Nicolai S
11
Anda dapat menyederhanakan ini dengan memasukkan argumen dari xargs dalam string perintah untuk bash with bash -c 'echo_var "{}"'. Jadi Anda tidak perlu _ {} di akhir.
fobia
16

Menggunakan GNU Parallel terlihat seperti ini:

#!/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

Jika Anda menggunakan versi 20170822 Anda bahkan tidak perlu export -fselama Anda menjalankan ini:

. `which env_parallel.bash`
seq -f "n%04g" 1 100 | env_parallel -P 10 echo_var {} 
Ole Tange
sumber
di mana saya mendapatkan shopt untuk osx?
Nick
nvm disetopt di zsh
Nick
Mendapatkan ini di bawah ini eerror Ole sh: parallel_bash_environment: line 67: unexpected EOF while looking for matching '' sh: parallel_bash_environment: line 79: error sintaks: akhir tak terduga file sh: error mengimpor definisi fungsi untuk 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: line 79: kesalahan sintaks: akhir tak terduga dari file / usr / local / bin / bash: error mengimpor definisi fungsi untuk `...
Nick
Anda telah shellaftershocked: Shellshock tidak memengaruhi GNU Parallel secara langsung. Solusi untuk shellshock, bagaimanapun, adalah: Ini benar-benar merusak --env dan trik env_parallel. Dipercaya diperbaiki dalam versi git: git.savannah.gnu.org/cgit/parallel.git/snapshot/…
Ole Tange
1
Saya suka jawaban ini, karena itu membuat saya menemukan alat paralel
JR Utily
10

Sesuatu seperti ini juga bisa digunakan:

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

Mungkin ini adalah praktik yang buruk, tetapi Anda jika Anda mendefinisikan fungsi dalam .bashrcskrip atau lainnya, Anda dapat membungkus file atau setidaknya definisi fungsi dengan pengaturan 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
xdhmoore
sumber