Разница между подпроцессами и подстановкой процессов

Вопрос или проблема

В bash я хочу присвоить текущую рабочую директорию переменной. Используя подпроцесс, я могу это сделать.

var=$(pwd)

echo $var
/home/user.name

Если я использую подстановку процесса следующим образом:

var=<(pwd)

echo $var
/dev/fd/63

Я понял, что подстановка процесса прежде всего используется, когда программа не принимает STDIN. Мне не ясно, что именно делает подстановка процесса и почему она присваивает /dev/fd/63 переменной var.

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

В вашем случае вывод из pwd в <(pwd) может быть найден по адресу /dev/fd/63. Этот файл прекращает существовать, как только команда, которая использует подстановку процесса, завершает выполнение (когда присвоение переменной var в вашем примере завершено).

Имя файла, возвращаемое подстановкой процесса, является именем дескриптора файла или именованного канала, а не обычного файла:

Подстановка процесса поддерживается на системах, которые
поддерживают именованные каналы (FIFO) или метод /dev/fd для именования открытых файлов.

Общее использование подстановки процессов — предварительная сортировка файлов для команды join:

$ join <( sort file1 ) <( sort file2 )

или для удаления колонок из файла (здесь колонка 2 удаляется из табуляционно-ограниченного файла, используя cut дважды и paste, чтобы сшить результат вместе):

$ paste <( cut -f 1 file ) <( cut -f 3- file )

Подстановка процесса более или менее является синтаксическим сокращением для избежания явного использования временных файлов.


Как подстановки команд, так и подстановки процессов выполняются в подпроцессах. Следующее показывает, что окружение в этих подпроцессах не влияет на окружение родительского шелла:

$ unset t
$ echo "$( t=1234; echo "$t" )"
1234
$ echo "$t"
(пустая строка)

Здесь echo получает 1234 в качестве строкового аргумента из подстановки команд.

$ unset t
$ cat <( t=4321; echo "$t" )
4321
$ echo "$t"
(пустая строка)

Здесь cat получает имя файла (именованный канал/дескриптор файла) в качестве своего аргумента. Файл содержит данные 4321.

Еще одно важное отличие между подстановкой команд и подстановкой процессов заключается в том, что первая не будет выводить ничего, пока команда не будет полностью выполнена, тогда как последняя немедленно выведет результирующие строки, как только они будут созданы. Следующий эксперимент демонстрирует это:

[amirha@login test]$ time head -n1 <(seq 1 1000000)
1

real    0m0.003s
user    0m0.002s
sys     0m0.001s
[amirha@login test]$ time head -n1 <<< "$(seq 1 1000000)"
1

real    0m0.546s
user    0m0.433s
sys     0m0.107s

Ответ или решение

Разница между подшеллами и подстановкой процессов в Bash

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

Подстановка команд (Command Substitution)

Подстановка команд осуществляется с помощью конструкции $(...). Этот метод позволяет выполнять команду и использовать её вывод в качестве значения. Например, в следующем коде:

var=$(pwd)
echo $var

Команда pwd выполняется, и её вывод (текущий рабочий каталог) сохраняется в переменной var. Когда мы используем echo $var, вывод будет, например, /home/user.name.

Подстановка команд завершает выполнение команды перед передачей результата обратно в родительский шелл. Из-за этого выполнение будет заблокировано до тех пор, пока не завершится команда, находящаяся внутри конструкции $(). Это также значит, что изменения переменных в подстановке не повлияют на родительский контекст.

Подстановка процессов (Process Substitution)

С другой стороны, подстановка процессов осуществляется с помощью синтаксиса <(...). Этот метод создает специальное имя файла, которое ссылается на вывод команды, позволяя другим командам читать этот вывод. Например:

var=< (pwd)
echo $var

Здесь вывод команды pwd не сохраняется непосредственно в переменной var. Вместо этого var будет иметь значение /dev/fd/63, которое является именем дескриптора файла (файлового потока). Этот дескриптор указывает на временное место, где находится вывод команды, и оно будет доступно для чтения до тех пор, пока не завершится работа программы, использующей подстановку процессов.

Процессная подстановка чаще всего применяется, когда программа не принимает стандартный ввод (STDIN). В подобных случаях можно использовать дескрипторы /dev/fd, чтобы "подключить" вывод одной команды в качестве ввода для другой. Например:

join <(sort файл1) <(sort файл2)

Эта конструкция позволяет использовать предварительно отсортированные данные для команды join без необходимости создания временных файлов.

Сравнение производительности и поведения

Существует еще одно важное различие: подстановка процессов производит вывод по мере его появления, в то время как подстановка команд возвращает все данные сразу по завершении выполнения. Это имеет значение в ситуациях, когда необходим быстрый доступ к первому значению:

# Замер времени для подстановки процессов
time head -n1 <(seq 1 1000000)
# Замер времени для подстановки команд
time head -n1 <<< "$(seq 1 1000000)"

Вывод показывает, что подстановка процессов работает быстрее, так как head может сразу прочитать первую строку продукта, не дожидаясь завершения всей команды.

Заключение

В заключение, подстановка команд и подстановка процессов являются мощными инструментами в Bash, каждый из которых имеет свои уникальные применения и поведение. Понимание различий между ними поможет вам более эффективно использовать оболочку, сосредоточившись на производительности и функциональности. Использование подстановки процессов для более сложных сценариев обработки данных может существенно увеличить вашу продуктивность при работе с Bash-скриптами.

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

Капча загружается...