การเรียกฟังก์ชันเชลล์ด้วย xargs


168

ฉันพยายามใช้ xargs เพื่อเรียกใช้ฟังก์ชันที่ซับซ้อนมากขึ้นในแบบคู่ขนาน

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

ส่งคืนข้อผิดพลาด

xargs: echo_var: No such file or directory

ความคิดใด ๆ เกี่ยวกับวิธีที่ฉันสามารถใช้ xargs เพื่อทำให้สิ่งนี้สำเร็จมิเช่นนั้นจะยินดีต้อนรับทางออกอื่น ๆ


2
อันตราย, ผู้ใช้ 1148366, อันตราย! อย่าใช้ bash สำหรับการเขียนโปรแกรมแบบขนาน - คุณจะพบปัญหามากมาย ใช้ C / C ++ และ pthreads หรือเธรด Java หรืออะไรก็ตามที่ทำให้คุณคิดมากและหนักใจกับสิ่งที่คุณทำเพราะการเขียนโปรแกรมแบบขนานนั้นต้องใช้ความคิดอย่างมาก
David Souther

27
@DavidSouther หากงานมีความเป็นอิสระเช่นแปลงไฟล์รูปภาพทั้งหมดเป็น png คุณไม่ต้องกังวล คือเมื่อคุณมีการซิงโครไนซ์ (รอให้ทุกคนเสร็จสิ้น) และการสื่อสารที่ทำให้ยุ่งเหยิง
ctrl-alt-delor

@DavidSouther - ฉันเป็น Java dev มานานแล้วและฉันทำงานหนักมาก และฉันก็ยังบอกคนต่อไป: เพื่อน ๆ อย่าให้เพื่อนเขียนสคริปทุบตี และยังฉันพบว่าตัวเองกำลังดูโพสต์นี้ / แก้ปัญหาเพราะ (หน้าเศร้า :() ฉันมีส่วนร่วมในการประมวลผลแบบขนานในทุบตีฉันสามารถทำได้อย่างง่ายดายใน groovy / java แย่!
Christian Bongiorno

ยังกล่าวถึงในunix.stackexchange.com/questions/158564/ …
Joshua Goldberg

คำตอบ:


172

การส่งออกฟังก์ชันควรทำ (ยังไม่ได้ทดสอบ):

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

คุณสามารถใช้ builtin printfแทนการใช้ภายนอกseq:

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

นอกจากนี้การใช้return 0และexit 0เช่นนั้นจะปิดบังค่าความผิดพลาดใด ๆ ที่อาจเกิดขึ้นจากคำสั่งก่อนหน้า นอกจากนี้หากไม่มีข้อผิดพลาดก็เป็นค่าเริ่มต้นและค่อนข้างซ้ำซ้อน

@phobic กล่าวถึงว่าคำสั่ง Bash สามารถทำให้ง่ายขึ้น

bash -c 'echo_var "{}"'

ย้าย{}ภายในโดยตรง แต่มันมีความเสี่ยงที่จะสั่งการฉีดตามที่ระบุไว้โดย @Sasha

นี่คือตัวอย่างทำไมคุณไม่ควรใช้รูปแบบฝังตัว:

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

อีกตัวอย่างหนึ่งที่ทำไม :

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

นี่คือผลลัพธ์ที่ใช้รูปแบบที่ปลอดภัย :

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

ก็เปรียบได้กับการใช้พารามิเตอร์ SQL คำสั่งที่จะหลีกเลี่ยงการฉีด

ฉันกำลังใช้dateในการทดแทนคำสั่งหรือคำพูดที่หลบหนีที่นี่แทนrmคำสั่งที่ใช้ในความคิดเห็นของ Sasha เพราะมันไม่ทำลาย


14
การสนทนาอีกเล็กน้อย: xargs ดำเนินการอินสแตนซ์ใหม่ที่สมบูรณ์ของกระบวนการที่มีชื่อ ในกรณีนี้คุณให้ชื่อecho_varซึ่งเป็นฟังก์ชั่นในสคริปต์นี้ไม่ใช่กระบวนการ (โปรแกรม) ใน PATH ของคุณ สิ่งที่โซลูชันของเดนนิสทำคือส่งออกฟังก์ชั่นสำหรับกระบวนการทุบตีเด็กที่จะใช้จากนั้นไปที่กระบวนการย่อยและดำเนินการที่นั่น
David Souther

7
ความสำคัญของอะไร_และ\หากไม่มีพวกเขามันก็ไม่ได้ผลสำหรับฉัน
Hashbrown

9
@Hashbrown: เครื่องหมายขีดล่าง ( _) ให้ตัวยึดตำแหน่งสำหรับargv[0]( $0) และแทบจะใช้อะไรก็ได้ ฉันคิดว่าฉันเพิ่มเครื่องหมายแบ็กสแลช - เซมิโคลอน ( \;) เนื่องจากการใช้ในการยกเลิกส่วน-execคำสั่งfindแต่มันใช้ได้กับฉันหากไม่มีที่นี่ ในความเป็นจริงหากใช้ฟังก์ชัน$@แทนฟังก์ชัน$1จะเห็นเครื่องหมายอัฒภาคเป็นพารามิเตอร์ดังนั้นจึงควรละเว้น
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

4
-i โต้แย้งกับ xargs ตั้งแต่เลิกใช้แล้ว ใช้ -I (ตัวใหญ่ i) แทน
Nicolai S

11
คุณสามารถลดความซับซ้อนนี้โดยรวมถึงการโต้แย้งจาก xargs bash -c 'echo_var "{}"'ในสตริงคำสั่งสำหรับทุบตีด้วย ดังนั้นคุณไม่ต้องการ _ {} ในตอนท้าย
phobic

16

การใช้ GNU Parallel ดูเหมือนว่า:

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

หากคุณใช้เวอร์ชั่น 20170822 คุณไม่จำเป็นต้องexport -fใช้ตราบใดที่ยังใช้งานได้:

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

ฉันจะได้รับ shopt สำหรับ osx ที่ไหน
Nick

nvm เป็น setopt ใน zsh
Nick

รับสิ่งนี้ด้านล่าง eerror Ole sh: parallel_bash_environment: line 67: unexpected EOF while looking for matching '' sh: ขนาน_bash_environment : บรรทัด 79: ข้อผิดพลาดทางไวยากรณ์: สิ้นสุดไฟล์ที่ไม่คาดคิด sh: ข้อผิดพลาดในการนำเข้าคำจำกัดความฟังก์ชันสำหรับparallel_bash_environment' /usr/local/bin/bash: parallel_bash_environment: line 67: unexpected EOF while looking for matching '' / usr / local / bin / bash: ขนาน_bash_environment: บรรทัด 79: ข้อผิดพลาดทางไวยากรณ์: file / usr / local / bin / bash: ข้อผิดพลาดการนำเข้านิยามฟังก์ชันสำหรับ `...
Nick

คุณได้รับ shellaftershocked แล้ว: Shellshock ไม่มีผลกับ GNU Parallel โดยตรง อย่างไรก็ตามการแก้ปัญหา shellshock ทำได้: มันทำลาย --env และเคล็ดลับ env_parallel อย่างเต็มที่ เชื่อกันว่าได้รับการแก้ไขในเวอร์ชันคอมไพล์: git.savannah.gnu.org/cgit/parallel.git/snapshot/…
Ole Tange

1
ฉันชอบคำตอบนี้เพราะมันทำให้ฉันค้นพบเครื่องมือคู่ขนาน
JR Utily

10

บางสิ่งเช่นนี้ควรใช้งานได้:

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

1

บางทีนี่อาจเป็นแนวปฏิบัติที่ไม่ดี แต่ถ้าคุณกำหนดฟังก์ชันใน.bashrcหรือสคริปต์อื่น ๆ คุณสามารถล้อมไฟล์หรืออย่างน้อยนิยามฟังก์ชันด้วยการตั้งค่า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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.