Замена процесса >(thing)
будет заменена именем файла. Это имя файла соответствует файлу, который подключен к стандартному входу thing
внутри подстановки.
Следующий пример будет лучшим примером его использования:
$ sort -o >(cat -n >/tmp/out) ~/.profile
Это отсортирует файл ~/.profile
и отправит вывод в cat -n
, который пронумерует строки и сохранит результат в /tmp/out
.
Итак, чтобы ответить на ваш вопрос :Вы получаете этот вывод, потому что echo
получает два аргумента 123
и /dev/fd/63
. /dev/fd/63
— это файл, подключенный к стандартному входу процесса cat
в замене процесса.
Немного изменив код вашего примера:
$ echo 101 > >(cat)
Это выведет только 101
на стандартный вывод (вывод echo
будет перенаправлен в файл, который служит входом для cat
, а cat
выдаст содержимое этого файла на стандартный вывод ).
Также обратите внимание, что в конвейере cmd1 | cmd2
cmd2
может вообще не работать в той же оболочке, что и cmd1
(, в зависимости от используемой вами реализации оболочки ). ksh93
работает так, как вы описываете (ту же оболочку ), в то время как bash
создает подоболочку для cmd2
(, если не установлен параметр оболочки lastpipe
и управление заданиями не активно ).
В отличие от чего?
Предположим, у вас есть программа, которую вы разделили на два файла. которые вы образно назвали file1.c
и file2.c
. Вы можете скомпилировать программу, запустив
cc file1.c file2.c -o yourprogram
Но это требует каждый раз перекомпиляции обоих файлов, даже если только один изменился. Вы можете разложить шаги компиляции на
cc -c file1.c
cc -c file2.c
cc file1.o file2.o -o yourprogram
а затем, когда вы редактируете один из файлов, перекомпилируйте только этот файл (и выполните шаг связывания независимо от того, что вы изменили ). Но что, если вы отредактируете один файл, , а затем другой, и вы забываете, что редактировали оба файла, а случайно перекомпилировать только один?
Кроме того, даже для двух файлов у вас есть около 60 символов команд. Набирать текст быстро становится утомительно. Хорошо, конечно, вы можете поместить их в сценарий, но потом вы каждый раз возвращаетесь к перекомпиляции. Или вы можете написать действительно причудливый, сложный скрипт, который проверяет какой файл (с )был изменен и делает только необходимые компиляции. Вы понимаете, к чему я клоню?
Даже в небольшом проекте может быть полезно контролировать логику зависимостей и автоматизировать сборку. Я также использовал его для запуска установок и деинсталляций, так что это был главный переключатель, сбрасывающий сцену.
Если вы связываете свое приложение из 2 исходных файлов(.c
), вам не нужно перекомпилировать каждый файл, а только измененный, если вы используете make.
Также приведу пример из мира BSD. У них есть структура Makefiles на основе системы -. Они предоставляют вам пути к системным каталогам и имеют цели для установки вашего программного обеспечения и справочных страниц.
Например, вы только что написали beer.c
приложение и руководство к нему под названием beer.6
. Вы создаетеMakefile
:
PROG= beer
MAN= beer.6
.include <bsd.prog.mk>
..и позвоните make install
. Он автоматически компилирует и устанавливает ваше приложение в /usr/bin
, а также компилирует и устанавливает вашу справочную страницу в то место, где man
может ее найти. Вы только что установили свое приложение с помощью одной простой команды!
Очень удобно и абсолютно прозрачно для всех, кто знаком с BSD. Гораздо лучше, чем ручной скрипт.
Многие другие люди вникают в детали более сложных make-файлов и много сложностей, которые с ними связаны. Обычно я использую make-файлы совсем по другой причине:
Даже если ваш проект действительно скучный и простой, и вы не используете make-файлы «правильно»:
all:
gcc main.c -o project
Мне не нужно думать об этом или относиться к нему иначе, чем к более сложному проекту:
all:
gcc libA.c libB.c main.c -o project2
Или, если я указал флаги (, например.-O2
)Мне не нужно помнить, что они были.
Кроме того, если вы начинаете с простого make-файла, а позже вам нужно выполнить слияние/рефакторинг, вам не нужно помнить о том, что каждый проект нужно собирать по-разному.
make
довольно надежно доступен. Если вы распространяете свой проект с makefile
, пользователи будут иметь простую справку о том, как выполнять задачи так же, как и вы. makefile
может использоваться не только для компиляции.
Возьмем, к примеру, проект, который не требует компиляции. Я помню, как работал над проектом Python, в котором была команда make для очистки всех файлов .pyc
, команда make для запуска тестов, команда для загрузки копии статических данных с сервера разработки и т. д.
Makefile
для моего очень маленького проекта:getPixelColor
Он делает именно то, что говорит его название, принимая два необязательных аргумента — координаты.
Мне особенно нравится, как там все становится зависимым.
COORDS ?= 0 0
CXX := g++-8
CXXFLAGS := -std=c++17 -Wall -Wextra -Werror -Wpedantic -pedantic-errors
LDLIBS := -lX11
RM := rm -f
BIN := getPixelColor
SRC := $(BIN).cpp
$(BIN): $(SRC)
$(CXX) $(CXXFLAGS) $(SRC) -o $(BIN) $(LDLIBS)
.PHONY: clean
clean:
$(RM) $(BIN)
.PHONY: run
run: $(BIN)
./$(BIN) $(COORDS)
Как видите, он может делать все, что вам нужно, не вводя ничего лишнего:
Вы можете запустить это так:
Очистить -старый бинарный файл:
make clean
Скомпилируйте новый бинарник:
make
Запустите исполняемый файл двумя способами:
координаты по умолчанию [0,0]
make run # equals COORDS='0 0'
любые заданные координаты
COORDS='5 6' make run
Makefiles иногда могут быть чрезвычайно полезны. Чем больше проект, тем больше выгода. Но даже с этим мой самый маленький проект на C++, как вы можете видеть на примерах, избавляет вас от многих головных болей.