Параметризация связанных вызовов утилиты в Bash


12

У меня есть UNIX-программа черного ящика, используемая в оболочке Bash, которая читает столбцы данных из stdin, обрабатывает их (применяя эффект сглаживания), а затем выводит в stdout. Я использую его по каналам UNIX, как

generate | smooth | plot  

Для большего сглаживания я могу повторить сглаживание, чтобы оно вызывалось из командной строки Bash как

generate | smooth | smooth | plot   

или даже

generate | smooth | smooth | smooth | smooth | smooth | smooth | smooth | smooth | smooth | smooth | plot

Это становится неприятным. Я хотел бы сделать обертку Bash, чтобы иметь возможность передавать smoothи передавать свои выходные данные обратно в новый экземпляр smoothпроизвольное число раз, что-то вроде

generate | newsmooth 5 | plot

вместо того

generate | smooth | smooth | smooth | smooth | smooth | plot

Моей первой попыткой был сценарий Bash, который генерировал временные файлы в текущем каталоге и удалял их, но это выглядело ужасно, когда я не находился в каталоге с доступом для записи, а также оставлял файлы мусора при прерывании.

У программы нет аргументов smooth.

Есть ли более элегантный способ «обернуть» такую ​​программу для параметризации количества вызовов?


1
Я надеюсь, что ваш пример является вынужденным делом ради вопроса, а не фактической необходимости
arielnmz

Ответы:


18

Вы могли бы обернуть это в рекурсивную функцию:

smooth() {
  if [[ $1 -gt 1 ]]; then # add another call to function
    command smooth | smooth $(($1 - 1)) 
  else
    command smooth # no further 
  fi
}

Вы бы использовали это как

generate | smooth 5 | plot

что будет эквивалентно

generate | smooth | smooth | smooth | smooth | smooth | plot

Это идеально, ведет себя именно так, как нужно. И теперь я узнал о ключевом слове bash «команда».
Дайан Уилбор

2
Между прочим, это тот же подход, который я использую в Как кодировать произвольно длинную цепочку каналов? - и задолго до этого при обработке длинных списков редактирования в xmlstarlet .
Чарльз Даффи

5

Если вы можете позволить себе вводить столько запятых, сколько smoothкоманд вам нужно, вы можете воспользоваться расширением скобок, разделенным запятыми.

TL; DR

Вся командная строка для вашего примера:

generate | eval 'smooth |'{,,,,} plot

Замечания:

  • добавить или удалить запятые, если вы хотите больше или меньше повторений smooth |
  • нет |раньше, plotпотому что это включено в последнюю smooth |строку, созданную расширением скобки
  • вы также можете предоставить аргументы smooth, если вы можете правильно их включить в заключенную в кавычки фиксированную часть, которая предшествует открытой фигурной скобке; в любом случае помните, что вы предоставляете их для всех повторений команды

Как это устроено

Разделенное запятыми расширение скобок позволяет динамически создавать строки, каждая из которых состоит из указанной фиксированной части плюс указанные переменные части. Он производит столько строк, сколько указано переменных частей, например, a{b,c,d}производит ab ac ad.

Небольшая хитрость заключается в том, что если вы скорее составите список пустых переменных частей, то есть только с запятыми внутри фигурных скобок, расширение скобок будет просто производить копии только фиксированной части. Например:

smooth{,,,,}

будет производить:

smooth smooth smooth smooth smooth

Обратите внимание, что 4 запятые производит 5 smoothстрок. Вот так работает этот Brace Expansion: он выдает в строку столько запятых плюс один.

Конечно, в вашем случае вам также нужно |разделить каждый из них smooth, поэтому просто добавьте его в фиксированную часть, но позаботьтесь о том, чтобы его правильно процитировать, чтобы оболочка не интерпретировала его сразу. То есть:

'smooth|'{,,,,}

будет производить:

'smooth|' 'smooth|' 'smooth|' 'smooth|' 'smooth|'

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

(Обратите также внимание, что для формирования фиксированной части вы также можете использовать двойные кавычки вместо одинарных кавычек, если вам нужно расширить переменные оболочки в фиксированной части. Просто позаботьтесь о дополнительном экранировании, которое требуется при появлении некоторых специальных символов оболочки внутри строки в двойных кавычках).

На этом этапе вам нужно eval применить к этой строке, чтобы оболочка окончательно интерпретировала ее как конвейерную команду, которой она должна быть.

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

generate | eval 'smooth |'{,,,,} plot

1
Существуют серьезные проблемы безопасности, если это используется в местах, где вызов параметризован. Посмотрите мой ответ о функции рекурсивного bash и итеративном построении «eval» строк: что работает лучше? на переполнении стека.
Чарльз Даффи

1
@CharlesDuffy Я полностью согласен с вашими опасениями по поводу подразумеваемых рисков использования, evalкогда вы предоставляете для оценки ненадежные, необработанные строки, то есть при использовании с переменными, которые могут содержать «неизвестный» контент, такой как случай, который вы связали. С другой стороны, evalэто также может быть очень удобно для быстрого «слежения» за командами, особенно при использовании в командной строке, как, например, в данном случае, где evalввод будет только литеральной строкой, набираемой вручную пользователем в человек
LL3

Как уже было видно в другом месте, вы всегда можете заменить eval strчто-то претенциозным и глупым, как . /dev/stdin <<<str. Это не только произведет впечатление на дураков, но и удержит @CharlesDuffy у вас за спиной ;-)
pizdelect

1
@pizdelect, вы можете внимательно прочитать предыдущий комментарий LL3 - он сбалансированный, нюансированный и мудрый. (Действительно, у моего собственного начального комментария были нюансы, которые вы, похоже, игнорировали; «если используется в случаях, когда вызов параметризован», это критическое различие: экземпляр LL3 не параметризован, что делает его безопасным).
Чарльз Даффи
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.