Как я могу использовать xargs для запуска функции в подстановке команд для каждого совпадения? - PullRequest
0 голосов
/ 19 февраля 2019

При написании функций Bash для замены строк я столкнулся со странным поведением при использовании xargs.Это на самом деле сводит меня с ума в настоящее время, так как я не могу заставить его работать.К счастью, я смог прибегнуть к следующему простому примеру:

Определить простую функцию, которая удваивает каждый символ данного параметра:

function subs { echo $1 | sed -E "s/(.)/\1\1/g"; }

Вызов функции:

echo $(subs "ABC")

Как и ожидалось, результат будет:

AABBCC

Теперь вызовите функцию, используя xargs:

echo "ABC" | xargs -I % echo $(subs "%")

Удивительно, но результат теперь таков:

ABCABC

Кажется, что команда sed внутри функции теперь обрабатывает всю строку как один символ.Почему это происходит и как это можно предотвратить?

Вы можете спросить, почему я вообще использую xargs.Конечно, это упрощенный пример, и фактический вариант использования гораздо сложнее.

В исходном сценарии использования у меня есть программа, которая производит много выходных данных.Я передаю вывод через несколько команд grep, чтобы получить интересующие строки.После этого я передаю строки в sed для извлечения нужных мне данных из строк.Поскольку некоторые преобразования, которые мне нужно выполнить для данных, слишком сложны, чтобы выполнять их только с помощью регулярных выражений, я бы хотел использовать для них функцию.Итак, моя первоначальная идея заключалась в том, чтобы просто подключиться к функции, но я не смог заставить ее работать и в итоге получил решение xargs.Моя первоначальная идея была примерно такой:

command | grep ... | grep ... | grep ... | sed ... | subs

Кстати: я делаю это не из командной строки, а из сценария.Функция определена в том же самом скрипте, в котором она используется.

Я использую Bash 3.2 (Mac OS X по умолчанию), так что мне не помогут изящные вещи Bash 4.x, извините.

Я буду рад всему, что может пролить свет на эту тему.

С наилучшими пожеланиями

Фрэнк

Ответы [ 2 ]

0 голосов
/ 19 февраля 2019

Если вам действительно нужно сделать это (и вы, вероятно, этого не сделаете, но мы не можем помочь без более репрезентативной выборки), подход с лучшей практикой может выглядеть следующим образом:

subs() { sed -E "s/(.)/\1\1/g" <<<"$1"; }
export -f subs

echo "ABC" | xargs bash -c 'for arg; do subs "$arg"; done' _
  • Использование echo "$(subs "$arg")" вместо просто subs "$arg" не добавляет ничего, кроме ошибок (рассмотрим, что произойдет, если один из ваших аргументов будет -n - и это предполагает относительно ручную echo; ониразрешено использовать обратную косую черту даже без аргумента -e и делать любые другие удивительные вещи).Вы могли бы сделать это выше, но это замедляет вашу программу и делает ее более склонной к неожиданному поведению;нет смысла.
  • Запуск export -f subs экспортировать вашу функцию в среду, чтобы она могла запускаться другими экземплярами bash, вызываемыми как дочерние процессы (все программы, вызываемые xargs, находятся вне вашей оболочки, поэтому онине может видеть локальные переменные или функции оболочки).
  • Без -I - то есть в режиме работы по умолчанию - xargs добавляет аргументы в конецКоманда это дано.Это позволяет намного более эффективный режим использования, где вместо вызова одной команды на строку ввода он передает как можно больше аргументов как можно меньшему числу подпроцессов.

    Это также позволяет избежать серьезных ошибок безопасности, которые могут произойтипри использовании xargs -I в сочетании с bash -c '...' или sh -c '...'.(Если вы когда-либо используете -I% sh -c '...%...', тогда ваши имена файлов станут частью вашего кода и могут использоваться для атак с использованием инъекций в вашей системе).

0 голосов
/ 19 февраля 2019

Это потому, что конструкция $(subs "%") расширяется оболочкой при разборе конвейера, поэтому xargs работает с echo %%.

...