Как дать список через запятую в качестве аргументов для следующей команды


9

У меня есть скрипт, s1который выводит список чисел, разделенных ',' например 1,2,3,4. Теперь я хочу дать эти числа сценарию в s2качестве аргументов, чтобы s2 запускался на каждом из них и выводил свой результат в отдельной строке. Например, если s2 умножает числа на два, это будет результат, который я ищу:

2
4
6
8

Что я делаю сейчас:

s1 | xargs -d "," | xargs -n1 s2

Но я чувствую, что делаю это так глупо! Итак, мой вопрос:

Как правильно это сделать?

Моя проблема с моим решением состоит в том, что он дважды вызывает xargs и дважды перебирает ввод, что, конечно, не является разумным для меня из-за производительности! Ответ xargs -d "," -n1кажется хорошим, но я не уверен, что он повторяется только один раз. Если это произойдет, пожалуйста, подтвердите это в своем ответе, и я приму это. Кстати, я бы предпочел не использовать Perl, поскольку он все еще повторяется дважды, а также Perl может не существовать во многих системах.


1
Если это работает, зачем называть это глупостью. Если время выполнения важно, тогда другое дело, оставьте здесь
Джордж Удосен

2
Попробуйте этоs1 | xargs -d "," -n1 s2
Джордж Удосен

1
Я подозреваю, что часть проблемы заключается в неправильном понимании воздействия итерации. Перебор элементов в чем-то вроде ассоциативного массива - это плохо из-за затрат на обход этой структуры данных, но «перебор» в целом не плох по своей сути. В частности, чтение данных построчно, как они поступают в STDIN, не является большой проблемой производительности. Проблема с производительностью здесь заключается в большей стоимости порождения нового процесса и настройки конвейера. Пока вы делаете это не часто (как в цикле), беспокойство по поводу производительности xargs, вероятно, является примером преждевременной оптимизации.
dannysauer

Ответы:


18

Это должно одинаково хорошо работать:

s1 | xargs -d "," -n1 s2

Прецедент:

printf 1,2,3,4 | xargs -d ',' -n1 echo

Результат:

1
2
3
4

Если s1выводится этот список с последующим символом новой строки, вы хотите удалить его, так как в противном случае последний вызов был бы 4\nвместо 4:

s1 | tr -d '\n' | xargs -d , -n1 s2

6

Если вы s2можете принять несколько аргументов, вы можете сделать:

(IFS=,; ./s2 $(./s1))

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

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

Чтобы вручную зациклить выходы и предоставить их индивидуально для s2, здесь демонстрируется сохранение и сброс IFS:

oIFS="$IFS"
IFS=,
for output in $(./s1); do ./s2 "$output"; done
IFS="$oIFS"

или запустить биты IFS в подоболочке как раньше:

(
IFS=,
for output in $(./s1); do ./s2 "$output"; done
)

1
Ты уверен насчет первого? bash -c 'IFS=, printf "%s\n" $(echo 1,2,3)'печатает 1,2,3на моей системе, т.е. нет разделения.
ilkkachu

Я немного перепроверю, но я подозреваю, что в зависимости от встроенных функций и внешних программ наблюдается другое поведение
Джефф Шаллер

то же самое с /usr/bin/printfи/bin/echo
ilkkachu

1
@ilkkachu Вы правы, разделение слов происходит до переназначения IFS, в этом случае (IFS=,; printf "%s\n" $(echo 1,2,3)), с другой стороны, должно работать.
Undercat аплодирует Monica

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.