Bash: подстановка процесса
Рассмотрим достаточно полезную штуку в консоли линукса (bash) как подстановка процесса.
Вообще подстановок в баше достаточно много всяких подстановок: результата выполнения команды арифметических операций, имен файлов, значений переменных и, конечно же, процессов.
Применима подстановка в первую очередь для того, чтобы избавиться от создания пайпов.А во вторую - для сохранения внешнего контекста при работе подпроцесса.
Избавляемся от пайпов
Имеем две команды, которые формируют некоторый список значений и мы хотим сделать diff или любую другую операцию, которая принимает на вход в качестве аргументов имена файлов.
Начнем с простого примера: хотим сравнить выводы двух команд.
$ sleep $((RANDOM%2)) && curl -v --silent https://google.com | grep date
На месте этой абсурдной команды может быть что угодно: обработка логов, данные телеметрии с сервера и т.п.
Как сделать diff? Два варианта: перенаправить выводы обеих команд в пайпы и использовать подстановку процессов в форме чтения вывода команды.
<(command)
% mkfifo f1
$ mkfifo f2
$ sleep $((RANDOM%2)) && curl -v --silent https://google.com | grep date > f1 &
$ sleep $((RANDOM%2)) && curl -v --silent https://google.com | grep date > f2 &
$ diff f1 f2
$ rm f1 f2
Либо более лаконично.
$ diff <(sleep $((RANDOM%2)) && curl -v --silent https://google.com | grep date) <(sleep $((RANDOM%2)) && curl -v --silent https://google.com | grep date)
Согласитесь, что второй вариант проще в создании. так как оболочка сама позаботилась о создании и удалении каналов. Другая форма использования перенаправить некоторый stdin на вход другой команды или цепочки команд.
>(command)
Допустим мы хотим записывать логи подключения разных групп устройств из udevadm в разные файлы. Можно запустить два процесса, но зачем?
$ udevadm monitor | tee >(grep card > card.log) >(grep usb > usb.log)
Тут не стоит забывать, что множественное перенаправление данных позволительно сделать при помощи tee, который берез stdin и пишет его во все указанные в качестве аргументов файлы.
Сохранение контекста
Подпроцесс - это одно из ключевых понятий bash и других оболочек.
$ export foo=baz
$ perl -e 'system "echo foo is \$foo"; $ENV{"foo"}="bar"; system "echo foo is \$foo"'
$ echo "$foo"
Perl запускается как дочерний процесс, родителем которого выступает bash (в нашем случае). Для данного процесса создается новый контекст, в который копируются переменные окружения и модификация контекста процесса не приводит к изменению контекста родителя.
Это приводит к тому, что модификация контекста внутри цепочки команд не отражается на глобальных переменных.
#!/usr/bin/env bash
i=0
sort test | while read line; do
i=$(($i+1))
echo $i
done
echo $i
В данном скрипте цикл while запускается как дочерний процесс, а следовательно у нас всегда будет выводиться 0. Хотя внутри самого цикла мы видим инкремент переменной.
Исправить ситуацию можно при помощи подстановки команд.
#!/usr/bin/env bash
i=0
while read line; do
i=$(($i+1))
echo $i
done < <(sort test)
echo $i
Литература
Категории: HowTo