Мне немного обидно за утилиту m4. Очень мощный текстовый процессор, но обычно она
находится в тени и там где она действительно могла бы составить конкуренцию
она проигрывает более привычным инструментам.
И я решила ее основательно помучить. Как говорится чем бы дитя
не тешилось лишь бы не плакало. Например поставила задачу написать калькулятор
Существует два варианта вызова. Первый может работать в потоке.
Но вот беда в этом варианте можно только определять макросы
Код: Выделить всё
cat - | m4 -Done=1 -Dtwo=2
one
1
two
2
Второй вариант предусматривает написание скриптов но подключить стандартный
ввод так чтобы он обрабатывался построчно набором команд не так то просто. Я испробовала
разные способы включая именованные каналы и все таки нашла приемлемый вариант через чтение
из стандартного потока ошибок. Все можно записать в файл, потом подключить скрипт и
вызвать функцию readline но так будет наглядней.
Код: Выделить всё
m4 -Q <<\eof
divert(`1')dnl
see you!
divert`'dnl
q - exit from calc
define(`q', `undivert`'m4exit')dnl
define(`plus', `+')dnl
define(`minos', `-')dnl
define(`times', `*')dnl
define(`divide', `/')dnl
define(`ou', `0')dnl
define(`one', `1')dnl
define(`two', `2')dnl
define(`three', `3')dnl
define(`four', `4')dnl
define(`five', `5')dnl
define(`six', `6')dnl
define(`seven', `7')dnl
define(`Z_0', ``ou'')dnl
define(`Z_1', ``one'')dnl
define(`Z_2', ``two'')dnl
define(`Z_3', ``three'')dnl
define(`Z_4', ``four'')dnl
define(`Z_5', ``five'')dnl
define(`Z_6', ``six'')dnl
define(`Z_7', ``seven'')dnl
define(`readline', `regexp(eval(esyscmd(`head -1 <&2')), `.*', `Z_\&')
ifelse(1, 1,`$0')')dnl
readline
eof
Это вроде отложенного вызова defer в golang
или atexit в Си
Код: Выделить всё
divert(`1')dnl
see you!
divert`'dnl
Код: Выделить всё
define(`q', `undivert`'m4exit')dnl
чтобы избежать прямой подстановки.
Код: Выделить всё
define(`plus', `+')dnl
...
Код: Выделить всё
define(`readline', `regexp(eval(esyscmd(`head -1 <&2')), `.*', `Z_\&')
ifelse(1, 1,`$0')')dnl
вызывает команду оболочки head -1 </dev/stderr через встроенную функцию esyscmd
которая помещает на ее место считанную строку, далее процессор подставляет
в строку определенные макросы в виде математического знака и чисел,
далее функция eval выполняет математическую операцию,
функция regexp к полученному результату подставляет префикс Z_ что бы избежать
бесконечного переопределения цифр и строк и в конечном итоге процессор подставляет
макросы экранированные дважды опять таки чтобы избежать переопределения.
Результат выглядит просто но на самом деле на пути к этому мне пришлось научиться
выводить строку посимвольно и столбиком и в строчку через пробел,
освоить бесконечные и терминированные циклы, есть уже написанные функции могущие разбить
строку по любому разделителю вроде сишной strtok, с выводом до рекурсивного цикла и после,
как говорится в хаскеле с правой и левой сверткой. Но вот помогите разобраться если мы читаем из stderr
а пишем по всему в stdin то как это работает? Я если честно просто уже от беспомощности
случайно набрела на этот вариант методом тыка. lsof говорит что дескриптор 2 универсальный