Вопрос или проблема
В 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-скриптами.