Gọi hàm shell với xargs


168

Tôi đang cố gắng sử dụng xargs để gọi một hàm phức tạp hơn song song.

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

Điều này trả về lỗi

xargs: echo_var: No such file or directory

Mọi ý tưởng về cách tôi có thể sử dụng xargs để thực hiện điều này, hoặc bất kỳ giải pháp nào khác sẽ được hoan nghênh.


2
Nguy hiểm, người dùng1148366, Nguy hiểm! Đừng sử dụng bash để lập trình song song - bạn sẽ gặp rất nhiều vấn đề. Sử dụng C / C ++ và pthreads, hoặc các luồng Java hoặc bất cứ điều gì khiến bạn suy nghĩ lâu và khó khăn về những gì bạn đang làm, bởi vì lập trình song song cần rất nhiều suy nghĩ để làm đúng.
David Souther

27
@DavidSouther Nếu các tác vụ là độc lập, chẳng hạn như chuyển đổi tất cả các tệp ảnh này sang png, thì đừng lo lắng. Đó là khi bạn đã đồng bộ hóa (ngoài việc chờ tất cả kết thúc) và giao tiếp trở nên lộn xộn.
ctrl-alt-delor

@DavidSouther - Tôi là một nhà phát triển Java lâu năm và tôi đã làm việc rất muộn. Và tôi tiếp tục nói với mọi người: Bạn bè đừng để bạn bè viết kịch bản bash. Tuy nhiên, tôi thấy mình đang xem bài đăng / giải pháp này bởi vì (khuôn mặt buồn bã :() Tôi đang tham gia vào quá trình xử lý song song trong bash. Tôi có thể dễ dàng thực hiện nó trong Groovy / java. Xấu!
Christian Bongiorno

Câu trả lời:


172

Xuất khẩu hàm nên làm điều đó (chưa được kiểm tra):

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

Bạn có thể sử dụng nội dung printfthay vì bên ngoài seq:

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

Ngoài ra, sử dụng return 0exit 0như thế che dấu bất kỳ giá trị lỗi nào có thể được tạo bởi lệnh trước nó. Ngoài ra, nếu không có lỗi, đó là mặc định và do đó hơi dư thừa.

@phobic đề cập rằng lệnh Bash có thể được đơn giản hóa thành

bash -c 'echo_var "{}"'

di chuyển {}trực tiếp bên trong nó. Nhưngdễ bị tiêm lệnh như được chỉ ra bởi @Sasha.

Dưới đây là một ví dụ tại sao bạn không nên sử dụng định dạng nhúng:

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

Một ví dụ khác về lý do tại sao không :

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

Đây là những gì đầu ra sử dụng định dạng an toàn :

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

Điều này có thể so sánh với việc sử dụng SQL được tham số hóa truy vấn để tránh tiêm .

Tôi đang sử dụng datethay thế lệnh hoặc trích dẫn thoát ở đây thay vì rmlệnh được sử dụng trong nhận xét của Sasha vì nó không phá hủy.


14
Thảo luận thêm một chút: xargs thực thi một thể hiện hoàn toàn mới của quy trình có tên. Trong trường hợp này, bạn cung cấp tên echo_var, đó là một chức năng trong tập lệnh này, không phải là một quá trình (chương trình) trong PATH của bạn. Những gì giải pháp của Dennis làm là xuất hàm cho các quy trình bash con sẽ sử dụng, sau đó chuyển sang quy trình con và thực thi ở đó.
David Souther

7
tầm quan trọng của _\nếu không có chúng thì nó không hoạt động với tôi
Hashbrown

9
@Hashbrown: Dấu gạch dưới ( _) cung cấp một trình giữ chỗ cho argv[0]( $0) và hầu hết mọi thứ có thể được sử dụng ở đó. Tôi nghĩ rằng tôi đã thêm dấu gạch chéo ngược ( \;) vì sử dụng nó trong việc chấm dứt -execmệnh đề trong find, nhưng nó hoạt động với tôi mà không có nó ở đây. Trong thực tế, nếu hàm được sử dụng $@thay vì $1sau đó nó sẽ xem dấu chấm phẩy là một tham số, vì vậy nó nên được bỏ qua.
Tạm dừng cho đến khi có thông báo mới.

4
-i đối số với xargs là không được chấp nhận. Sử dụng -I (vốn i) thay thế.
Nicolai S

11
Bạn có thể đơn giản hóa điều này bằng cách đưa đối số từ xargs vào chuỗi lệnh cho bash với bash -c 'echo_var "{}"'. Vì vậy, bạn không cần _ {} ở cuối.
Phobic

16

Sử dụng GNU Parallel giống như thế này:

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

Nếu bạn sử dụng phiên bản 20170822, bạn thậm chí không export -fphải chạy miễn là bạn đã chạy nó:

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

Tôi lấy shopt cho osx ở đâu?
Nick

nvm nó được thiết lập trong zsh
Nick

Lấy cái này bên dưới eerror Ole sh: parallel_bash_environment: line 67: unexpected EOF while looking for matching '' sh : allel_bash_en môi trường : dòng 79: lỗi cú pháp: kết thúc không mong muốn của tệp sh: lỗi nhập định nghĩa hàm cho parallel_bash_environment' /usr/local/bin/bash: parallel_bash_environment: line 67: unexpected EOF while looking for matching '' / usr / local / bin / bash :allel_bash_en môi trường: dòng 79: lỗi cú pháp: kết thúc không mong muốn file / usr / local / bin / bash: lỗi nhập định nghĩa hàm cho `...
Nick

Bạn đã được shellaftershocked: Shellshock không ảnh hưởng trực tiếp đến GNU Parallel. Tuy nhiên, giải pháp cho shellshock đã làm: Nó hoàn toàn phá vỡ --env và thủ thuật env_pool. Nó được cho là đã được sửa trong phiên bản git: git.savannah.gnu.org/cgit/abul.git/snapshot/ Kẻ
Ole Tange

1
Tôi thích câu trả lời này, vì nó khiến tôi khám phá công cụ song song
JR Utily 9/2/2015

10

Một cái gì đó như thế này cũng hoạt động:

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

1

Có thể đây là một thực tiễn tồi, nhưng nếu bạn đang xác định các hàm trong một .bashrctập lệnh hoặc tập lệnh khác, bạn có thể bọc tập tin hoặc ít nhất là các định nghĩa hàm với một cài đặt 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.