xargs - добавить каждый аргумент с параметром

12

Я знаю, что, учитывая l="a b c",

echo $l | xargs ls

доходность

ls a b c

Какая конструкция дает

mycommand -f a -f b -f c
не-а-пользователь
источник

Ответы:

13

Один из способов сделать это:

echo "a b c" | xargs printf -- '-f %s\n' | xargs mycommand

Это предполагает a, bи cне содержат пробелы, переводы строк, кавычки или слэш. :)

С GNU findutilвы можете справиться с общим случаем, но это немного сложнее:

echo -n "a|b|c" | tr \| \\0 | xargs -0 printf -- '-f\0%s\0' | xargs -0 mycommand

Вы можете заменить |разделитель с каким - либо другим символом, который не появляется в a, bили c.

Редактировать: как отмечает @MichaelMol , с очень длинным списком аргументов существует риск переполнения максимальной длины аргументов, которые могут быть переданы mycommand. Когда это произойдет, последний xargsразделит список и запустит еще одну копию mycommand, и есть риск, что он останется неопределенным -f. Если вы беспокоитесь об этой ситуации, вы можете заменить последнее xargs -0выше чем-то вроде этого:

... | xargs -x -0 mycommand

Это не решит проблему, но прервет работу, mycommandкогда список аргументов станет слишком длинным.

Сату Кацура
источник
1
Вы рискуете превзойти ARG_MAXи получить -fотдельный от своего парного параметра риск .
Майкл Мол
@MichaelMol Это хороший момент, но я не думаю, что есть какой-либо осмысленный способ справиться с этой ситуацией, не зная больше mycommand. Вы всегда можете добавить -xк последнему xargs.
Satō
Я думаю, что правильное решение, вероятно, не использовать xargsвообще, а просто использовать, findесли оно может быть использовано. Это решение опасно; Вы должны по крайней мере предупредить о случае отказа в своем ответе.
Майкл Мол
@MichaelMol Я действительно не понимаю, как findбыло бы лучше общее решение, особенно когда начальные аргументы не являются именами файлов. :)
Satō
Мы не знаем, каковы начальные аргументы; мы видим только приведенный пример, а не сценарий, который вдохновил вопрос. Интуиция предполагает, что с именем аргумента -fи с примером инструмента, lsиспользуемого для иллюстрации, @ not-a-user имеет дело с именами файлов. И учитывая findпредлагает -execаргумент, который позволяет вам создавать командную строку, это нормально. (Пока mycommandразрешено выполнять более одного раза. Если это не так, у нас есть еще одна проблема с использованием xargsздесь ...)
Майкл Мол
5

Лучший способ решить эту проблему (ИМО) будет:

  • в zsh:

    l=(a b c)
    mycommand -f$^l

    или используя zip-массив, чтобы аргумент не был прикреплен к опции:

    l=(a b c) o=(-f)
    mycommand "${o:^^l}"

    Таким образом, это все еще работает, если lмассив содержит пустые элементы или элементы, содержащие пробелы или любой другой проблемный символ для xargs. Пример:

    $ l=(a '' '"' 'x y' c) o=(-f)
    $ printf '<%s>\n' "${o:^^l}"
    <-f>
    <a>
    <-f>
    <>
    <-f>
    <">
    <-f>
    <x y>
    <-f>
    <c>
  • в rc:

    l=(a b c)
    mycommand -f$l
  • в fish:

    set l a b c
    mycommand -f$l

(AFAIK, rcи не fishимеют архива массива)

С подобными Bourne-подобными оболочками в старом стиле bashвы всегда можете это сделать (по-прежнему допуская любой символ в элементах $@массива):

set -- a b c
for i do set -- "$@" -f "$i"; shift; done
mycommand "$@"
Стефан Шазелас
источник
1
Другой способ сделать это в bash - использовать именованную переменную массива. for i; do args+=('-f' "$i");done; mycommand "${args[@]}", IDK, если это быстрее, но добавление 2 элементов в массив выглядит так, как будто это должно быть O (n), в то время как ваш setцикл, вероятно, копирует и повторно анализирует накопленный список аргументов каждый раз (O (n ^ 2)).
Питер Кордес