HowTo, linux

Bash: подстановка процесса

2018-07-03-22:14:15_267x133Рассмотрим достаточно полезную штуку в консоли линукса (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

Литература

 

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход /  Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход /  Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход /  Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход /  Изменить )

w

Connecting to %s