Я использую GNU Parallel и хочу понять — как мне передать отдельную строку каждой параллельной команде?
В качестве примера,Параллельная документация GNUпоказано, как переместить файлы из текущего каталога в другой:
ls | parallel mv {} destdir
Так есть ли способ получить/распечатать каждый файл по отдельности, который был передан в параллель?
Случай параллельной обработки
Мне нужно выполнить параллельную обработку проверки нескольких сайтов и записать
- код возврата http (2xx, 4xx, 5xx)
- Исходный URL-адрес
- Конечный целевой URL
- код выхода curl
Вот код, который это делает:
unset return_code_array
unset destination_url_array
unset exit_code_array
while read -r return_code_var destination_url_var exit_code_var; do
destination_url_array+=("$destination_url_var")
exit_code_array+=("$exit_code_var")
return_code_array+=("$return_code_var")
done < <(printf '%s\n' "${all_valid_URLs_array[@]}" | parallel -j 20 -k 'curl --max-time 20 -sL -o /dev/null -w "%{response_code} %{url_effective} " {}; printf "%s %s\n" "$?" ')
В результате у меня есть три массива, и они содержат код возврата HTTP, конечный целевой URL и статус кода выхода curl для каждой соответствующей строки для all_valid_URLs_array
записей. В то же время мне нужно выполнить некоторую обработку для каждого destination_url_var
— например, сравнить, соответствует ли он исходному URL, но я понятия не имею, как получить строку, которая была передана в parallels.
В настоящее время я запускаю второй цикл после предыдущего для такой обработки, но хочу знать, возможно ли это.
Спасибо.
решение1
В вашем примере 'curl … {}; printf "%s %s\n" "$?" '
(почему второй %s
?) есть кусок кода оболочки в одинарных кавычках. В нем вы можете использовать {}
более одного раза:
curl … {}; printf "%s %s\n" "$?" {}
Альтернативно создайте переменную и используйте ее столько раз, сколько захотите. Имя переменной может быть описательным, это преимущество. Есть еще одно преимущество: в общем случае то, что заменяется, {}
может быть длинной строкой, многократная замена может раздуть код, parallel
который будет передан определенным оболочкам. ИМХО, лучше заменить один раз и позволить оболочке сохранить строку и использовать ее повторно:
source_URL={}; curl … "$source_URL"; printf "%s %s\n" "$?" "$source_URL"
В случае GNU parallel
безопасно встраивать {}
в код оболочки. Это исключение, явно упомянутое в этом ответе:Никогда не встраивайте {}
в шелл-код!. Вы, вероятно, уже знаете это, замечание адресовано широкой аудитории.
Обратите внимание, что вам нужно настроить ваш read
основной цикл, теперь он должен считыватьсячетырепеременные. Таким образом вы перенесете исходный URL изнутри parallel
в основной цикл, где вы сможете его сравнить destination_url_var
или сделать с ним все, что захотите.
Однако при таком подходе «все, что вы хотите» не распараллеливается.
Если вы захватите вывод из curl
отдельных переменных внутри кода оболочки, запущенного parallel
(вместо того, чтобы просто вывести его для захвата за пределами parallel
), то вы сможете выполнить сравнение (или что-то еще, что вам нужно)там, параллельно. И напримерprintf
условно. Вам решать, где реализовать желаемую логику, главное, чтобы внутренняя часть parallel
генерировала вывод в форме, ожидаемой внешней частью read
.
Переданный код оболочки parallel
все еще должен быть заключен в одинарные кавычки. По мере его роста вам может потребоваться использовать (внедрять) одинарные кавычки в этот самый код; тогда цитирование станет несколько сложным и менее читаемым. В такой ситуации рассмотрите возможность перемещения кода в отдельный скрипт, где вы сможете цитировать независимо. Вы вызовете его из основного скрипта следующим образом:
while read … ; done < <( … | parallel -j 20 -k 'path/to/separate_script {}' )
Внутри separate_script
заменяемая строка {}
будет доступна как $1
(не забудьтезаключить его в двойные кавычки).