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

FAC3
источник
2
Danger, user1148366, Danger! Не используйте bash для параллельного программирования - вы столкнетесь с таким количеством проблем. Используйте C / C ++ и pthreads, или потоки Java, или что-нибудь, что заставляет вас долго и усердно думать о том, что вы делаете, потому что параллельное программирование требует много мыслей, чтобы стать правильным.
Дэвид Саутер
27
@DavidSouther Если задачи независимы, например, конвертировать все эти файлы изображений в PNG, не беспокойтесь. Это когда у вас есть синхронизация (за пределами ожидания, чтобы все закончить) и общение, что становится грязным.
Ctrl-Alt-Delor
@DavidSouther - я давний Java-разработчик и в последнее время работаю в отличной среде. И я продолжаю говорить людям: друзья не позволяют друзьям писать сценарий bash. И все же, я нахожу, что смотрю на этот пост / решение, потому что (печальное лицо :() Я занимаюсь параллельной обработкой в ​​bash. Я мог бы легко сделать это в groovy / java. Плохо!
Кристиан Бонджорно
Также обсуждается в unix.stackexchange.com/questions/158564/…
Джошуа Голдберг

Ответы:

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), и там можно использовать практически все, что угодно. Я думаю, что я добавил backslash-точку с запятой ( \;) из-за его использования в конце -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?
Ник
нвм это сетопт в зш
ник
Получение этого ниже eerror Ole sh: parallel_bash_environment: line 67: unexpected EOF while looking for matching '' sh: parallel_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: parallel_bash_environment: строка 79: синтаксическая ошибка: неожиданный конец file / usr / local / bin / bash: ошибка импорта определения функции для `...
Ник
Вы были поражены шеллафтершоком: шелл шок напрямую не влиял на 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
xdhmoore
источник