Виклик функцій оболонки за допомогою 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, або все, що змушує вас довго і наполегливо думати над тим, що ви робите, тому що паралельне програмування потребує багато думок, щоб правильно.
Девід Сутер

27
@DavidSouther Якщо завдання незалежні, наприклад, перетворити всі ці файли зображень у png, не хвилюйтесь. Саме тоді, коли у вас синхронізація (поза тим, як чекати, коли все закінчиться) і спілкування, вона стає безладним.
ctrl-alt-delor

@DavidSouther - Я тривалий час Java-розробник, і працюю в грубії пізнього часу. І я продовжую говорити людям: Друзі не дозволяють друзям писати баш сценарій. І все-таки я переживаю цю посаду / рішення, тому що (сумне обличчя :() я займаюся паралельною обробкою в баші. Я міг би це легко робити в groovy / java. Погано!
Крістіан

Відповіді:


172

Експорт функції повинен виконувати це (неперевірено):

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

Ви можете використовувати вбудований 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команди, що використовується в коментарі Саші, оскільки це не руйнує.


14
Трохи більше обговорення: xargs виконує абсолютно новий екземпляр процесу з назвою. У цьому випадку ви надаєте ім'я echo_var, яке є функцією в цьому скрипті, а не процес (програма) у вашій PATH. Що рішення Денніса - це експорт функції, яка використовується для дочірніх процесів bash, а потім пересилається в підпроцес і виконується там.
Девід Сутер

7
яке значення _і \без них для мене це не
спрацювало

9
@Hashbrown: Підкреслення ( _) надає місце для місця argv[0]( $0), і майже все, що там можна використовувати. Я думаю, що я додав зворотну косу крапку з комою ( \;) через його використання для припинення -execпункту в find, але він працює для мене без нього тут. Насправді, якби функція використовувалась $@замість $1цього, вона б бачила крапку з комою як параметр, тому її слід опустити.
Призупинено до подальшого повідомлення.

4
-i аргумент xargs з тих пір застарілий. Використовуйте натомість -I (капітал i).
Микола С

11
Ви можете спростити це, включивши аргумент з xargs в командний рядок для bash з bash -c 'echo_var "{}"'. Отже, вам не потрібно _ {} наприкінці.
фобічний

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 {} 

де мене шукають для OSX?
Нік

nvm це setopt в zsh
Нік

Отримання цього нижче 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

Ви вже знаходитесь під час обстрілу: Shellshock не впливав безпосередньо на GNU Parallel. Однак рішення Shellshock так і зробило: це абсолютно зламало --env і трюк env_parallel. Вважається, що це зафіксовано у версії git: 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.