вызов функции оболочки с использованием параллели со списком имен файлов в кавычках в качестве входных данных - PullRequest
1 голос
/ 21 февраля 2020

Использование Bash.

У меня есть экспортированная функция оболочки, которую я хочу применить ко многим файлам.

Обычно я использую xargs, но синтаксис такой (см. здесь ) слишком уродлив для использования.

...... | xargs -n 1 -P 10 -I {} bash -c 'echo_var "$@"' _ {}

В этом обсуждении parallel имел более простой синтаксис:

..... | parallel -P 10 echo_var {}

Теперь я столкнулся со следующей проблемой: список файлов, к которым я хочу применить свою функцию, представляет собой список файлов в одной строке, каждый из которых заключен в кавычки и разделен пробелами таким образом: "file 1" "file 2" "file 3".

как мне передать этот разделенный пробелами список в parallel?

Я могу скопировать список, используя echo для тестирования.

Например,

echo '"file 1" "file 2" "file 3"'|parallel -d " " my_function {}

, но я не могу заставить это работать.

Как это исправить?

Ответы [ 2 ]

1 голос
/ 21 февраля 2020

Проблема сводится к тому, что значения могут содержать пробел, а пробел является разделителем значений. Поэтому нам нужно что-то, что может анализировать входные данные в отдельные значения, содержащие пробел. Поскольку они заключены в кавычки bash, очевидным выбором является использование bash для удаления значений из кавычек.

У вас есть несколько опций:

(echo "file 1";
 echo "file  2";
 echo "file \"name\" \$(3)") | parallel my_function

printf "%s\n" "file 1" "file  2" "file \"name\" \$(3)" |
  parallel my_function

Если входные данные находятся в переменной:

var='"file 1" "file  2" "file \"name\" \$(3)"'
eval 'printf "%s\n" '"$var" |
  parallel my_function

Или вы можете преобразовать переменную в массив:

var='"file 1" "file  2" "file \"name\" \$(3)"'
eval arr=("$var")

И если входные данные находятся в массиве:

parallel my_function ::: "${arr[@]}"
1 голос
/ 21 февраля 2020

Как это исправить?

Вы должны выбрать уникальный разделитель.

echo 'file 1|file 2|file 3' | xargs -d "|" -n1 bash -c 'my_function "$@"' --
echo 'file 1^file 2^file 3' | parallel -d "^" my_function

Самое безопасное - использовать нулевой байт в качестве разделителя:

echo -e 'file 1\x00file 2\x00file 3' | xargs -0 ' -n1 bash -c 'my_function "$@"' --
printf "%s\0" 'file 1' 'file 2' 'file 3' | parallel -0 my_function

Лучше всего хранить ваши элементы в массиве bash и использовать поток, разделенный нулями, для их обработки:

files=("file 1" "file 2" "file 3")
printf "%s\0" "${files[@]}" | xargs -0 -n1 bash -c 'my_function "$@"' --
printf "%s\0" "${files[@]}" | parallel -0 my_function

Обратите внимание, что пустые массивы будут запускать функцию без каких-либо аргументы. Иногда предпочтительно использовать опцию -r --no-run-if-empty, чтобы не запускать функцию, когда ввод пуст. --no-run-if-empty поддерживается parallel и является расширением GNU в xargs (xargs на BSD и на OSX не имеют --no-run-if-empty).

Примечание: xargs по умолчанию анализирует ', " и \. Вот почему следующее возможно и будет работать:

echo '"file 1" "file 2" "file 3"' | xargs -n1 bash -c 'my_function "$@"' --
echo "'file 1' 'file 2' 'file 3'" | xargs -n1 bash -c 'my_function "$@"' --
echo 'file\ 1 file\ 2 file\ 3' | xargs -n1 bash -c 'my_function "$@"' --

И это может привести к некоторым странным вещам, поэтому не забывайте почти всегда указывать параметр -d для xargs:

$ # note \x replaced by single x
$ echo '\\a\b\c' | xargs
\abc
$ # quotes are parsed and need to match
$ echo 'abc"def' | xargs
xargs: unmatched double quote; by default quotes are special to xargs unless you use the -0 option
$ echo "abc'def" | xargs
xargs: unmatched single quote; by default quotes are special to xargs unless you use the -0 option

xargs - это портативный инструмент, доступный повсюду, в то время как parallel - это программа GNU, которую нужно устанавливать отдельно.

...