Почему $ ls > ls.out
ls.out включается в список имен файлов в текущем каталоге? Почему это было выбрано? Почему не иначе?
command-line
bash
redirect
Эдвард Торвальдс
источник
источник
ls > ../ls.out
Ответы:
При оценке команды
>
перенаправление сначала разрешается: поэтому к тому времени, какls
запускается, выходной файл уже создан.Это также причина, по которой чтение и запись в один и тот же файл с помощью
>
перенаправления в пределах одной и той же команды усекает файл; к моменту запуска команды файл уже обрезан:Уловки, чтобы избежать этого:
<<<"$(ls)" > ls.out
(работает для любой команды, которая должна быть запущена до разрешения перенаправления)Подстановка команд выполняется до оценки внешней команды, поэтому
ls
выполняется доls.out
создания:ls | sponge ls.out
(работает для любой команды, которая должна быть запущена до разрешения перенаправления)sponge
записывает в файл только после завершения остальной части канала, поэтомуls
выполняется доls.out
создания (sponge
предоставляется сmoreutils
пакетом):ls * > ls.out
(работает дляls > ls.out
конкретного случая)Расширение имени файла выполняется до разрешения перенаправления, поэтому
ls
будет работать с его аргументами, которые не будут содержатьls.out
:О том, почему переназначения разрешаются до программы / скрипта / все , что бежать, я не вижу причин , почему конкретные это обязательное сделать это, но я вижу две причины , почему это лучше сделать так:
если вы не перенаправите STDIN заранее, программа / скрипт / все остальное будет удерживаться до перенаправления STDIN;
предварительное перенаправление STDOUT не обязательно должно делать буфер оболочки обязательным для вывода программы / скрипта / что угодно, пока не будет перенаправлен STDOUT;
Таким образом, пустая трата времени в первом случае и пустая трата времени и памяти во втором случае.
Это то, что происходит со мной, я не утверждаю, что это реальные причины; но я думаю, что в целом, если бы у кого-то был выбор, они все равно пошли бы с перенаправлением раньше по вышеуказанным причинам.
источник
От
man bash
:Первое предложение предполагает, что вывод сделан для того, чтобы идти куда-то, кроме
stdin
перенаправления, непосредственно перед выполнением команды. Таким образом, чтобы быть перенаправленным в файл, файл сначала должен быть создан самой оболочкой.Чтобы избежать файла, я предлагаю вам перенаправить вывод сначала в именованный канал, а затем в файл. Обратите внимание на использование
&
для возврата контроля над терминалом пользователюНо почему?
Подумайте об этом - где будет выход? Программа имеет такие функции , как
printf
,sprintf
,puts
, которые все по умолчанию идти кstdout
, но может их выход пропадут в файл , если файл не существует , в первую очередь? Это как вода. Можете ли вы получить стакан воды, не ставя стакан под кран сначала?источник
Я не согласен с текущими ответами. Выходной файл должен быть открыт перед выполнением команды, иначе команде некуда будет записывать свой вывод.
Это потому, что "все это файл" в нашем мире. Вывод на экран - SDOUT (он же дескриптор файла 1). Для приложения, чтобы записать в терминал, он открывает fd1 и пишет в него как файл.
Когда вы перенаправляете вывод приложения в оболочку, вы изменяете fd1, чтобы он фактически указывал на файл. Когда вы передаете по каналу, вы изменяете STDOUT одного приложения, чтобы оно стало STDIN другого (fd0).
Но все это приятно говорить, но вы можете легко посмотреть, как это работает
strace
. Это довольно тяжелый материал, но этот пример довольно короткий.Внутри
strace.out
мы можем увидеть следующие основные моменты:Это открывается
ls.out
какfd3
. Пиши только. Усекает (перезаписывает), если существует, в противном случае создает.Это немного жонглирования. Мы отключаем STDOUT (fd1) от fd10 и закрываем его. Это потому, что мы не выводим что-либо в реальный STDOUT с помощью этой команды. Он заканчивается дублированием дескриптора записи
ls.out
и закрытием исходного.Это он ищет исполняемый файл. Урок, возможно, чтобы не иметь длинный путь;)
Затем команда выполняется, и родитель ждет. Во время этой операции любой STDOUT будет фактически сопоставлен с дескриптором открытого файла
ls.out
. Когда дочерний процесс выдает сообщениеSIGCHLD
, он сообщает родительскому процессу, что он завершен, и может возобновить его. Это заканчивается немного больше жонглирования и закрытияls.out
.Почему так много жонглирования? Нет, я тоже не совсем уверен.
Конечно, вы можете изменить это поведение. Вы можете буферизовать в памяти что-то вроде этого,
sponge
и это будет невидимым из исходящей команды. Мы по-прежнему влияем на файловые дескрипторы, но не видимыми для файловой системы.источник
Также есть хорошая статья о реализации перенаправления и конвейерных операторов в оболочке . Который показывает, как перенаправление может быть реализовано так, чтобы
$ ls > ls.out
могло выглядеть так:источник