Почему делает $ ls > ls.out
заставьте 'ls.out' быть включенным в список названий файлов в текущем каталоге? Почему это было выбрано, чтобы быть? Почему не иначе?
При оценке команды >
перенаправление разрешено сначала: таким образом к этому времени ls
работает выходной файл уже был создан.
Это - также причина почему, читая и при записи в тот же файл с помощью a >
перенаправление в рамках той же команды усекает файл; к тому времени, когда команда петляет уже, было усеченным:
$ echo foo >bar
$ cat bar
foo
$ <bar cat >bar
$ cat bar
$
Приемы для предотвращения этого:
<<<"$(ls)" > ls.out
(работы для любой команды, которая должна работать перед перенаправлением, разрешены),
Замена команды выполняется, прежде чем внешняя команда оценена, таким образом, ls
выполняется прежде ls.out
создается:
$ ls
bar foo
$ <<<"$(ls)" > ls.out
$ cat ls.out
bar
foo
ls | sponge ls.out
(работы для любой команды, которая должна работать перед перенаправлением, разрешены),
sponge
записи в файл только, когда остальная часть канала закончила выполняться, таким образом, ls
выполняется прежде ls.out
создается (sponge
обеспечивается moreutils
пакет):
$ ls
bar foo
$ ls | sponge ls.out
$ cat ls.out
bar
foo
ls * > ls.out
(работы для ls > ls.out
конкретный случай)
Расширение имени файла выполняется, прежде чем перенаправление разрешено, таким образом, ls
будет работать на его аргументах, которые не будут содержать ls.out
:
$ ls
bar foo
$ ls * > ls.out
$ cat ls.out
bar
foo
$
На том, почему разрешены перенаправления перед программой / сценарий / независимо от того, что выполняется, я не вижу определенных оснований, почему это обязательно, чтобы сделать так, но я вижу два основания, почему лучше сделать так:
не перенаправление STDIN заранее сделал бы программу / сценарий / независимо от того, что содержит, пока STDIN не перенаправляется;
не перенаправление STDOUT заранее должен обязательно заставить оболочку буферизовать программу / сценарий / независимо от того, что производится, пока STDOUT не перенаправляется;
Так пустая трата времени в первом случае и пустая трата времени и память во втором случае.
Это, что происходит со мной, я не утверждаю, что это истинные причины; но я предполагаю, что, в целом, если бы у каждого был выбор, они пошли бы с перенаправлением прежде так или иначе по вышеупомянутым причинам.
Существует также хорошая статья приблизительно Реализация перенаправления и операторов канала в оболочке . То, которое показывает, как перенаправление могло быть реализовано так $ ls > ls.out
, могло быть похожим:
main(){
close(1); // Release fd no - 1
open("ls.out", "w"); // Open a file with fd no = 1
// Child process
if (fork() == 0) {
exec("ls");
}
}
От man bash
:
ПЕРЕНАПРАВЛЕНИЕ
Прежде чем команда выполняется, ее ввод и вывод может быть перенаправлен с помощью специальной нотации, интерпретируемой оболочкой. Перенаправление позволяет дескрипторам файлов команд быть дублированными, открытыми, закрытыми, сделанными относиться к различным файлам, и может изменить файлы чтения команды от и пишет в.
Первое предложение, предполагает, что производит, сделан пойти куда-нибудь кроме stdin
с перенаправлением прямо, прежде чем выполняется команда. Таким образом, чтобы быть перенаправленным в файл, файл должен сначала быть создан самой оболочкой.
Чтобы постараться не иметь файл, я предлагаю, чтобы Вы перенаправили вывод к именованному каналу сначала, и затем в файл. Отметьте использование &
возвратить управление терминалом пользователю
DIR:/xieerqi
skolodya@ubuntu:$ mkfifo /tmp/namedPipe.fifo
DIR:/xieerqi
skolodya@ubuntu:$ ls > /tmp/namedPipe.fifo &
[1] 14167
DIR:/xieerqi
skolodya@ubuntu:$ cat /tmp/namedPipe.fifo > ls.out
Но почему?
Думайте об этом - где будет вывод? Программа имеет функции как printf
, sprintf
, puts
, в который все по умолчанию переходят stdout
, но их вывод может закончиться в файл, если файл не существует во-первых? Это похоже на воду. Можно ли получить стекло воды, не помещая стекло под краном сначала?
Я не не соглашаюсь с текущими ответами. Выходной файл должен быть открыт, прежде чем выполнения команды или команда нигде не должны будут писать ее вывод.
Это вызвано тем, что "все - файл" в нашем мире. Выведенный на экран SDOUT (иначе дескриптор файла 1). Чтобы приложение записало в терминал, оно открывает fd1 и пишет в него как файл.
При перенаправлении вывода приложения в оболочке Вы изменяете fd1, таким образом, он на самом деле указывает на файл. Когда Вы передаете Вас по каналу, изменяют STDOUT одного приложения для становления чьим-либо STDIN (fd0).
Но это - все хорошее высказывание, что, но можно довольно легко посмотреть на то, как это работает с strace
. Это - довольно тяжелый материал, но этот пример довольно короток.
strace sh -c "ls > ls.out" 2> strace.out
В strace.out
мы видим следующие выделения:
open("ls.out", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
Это открывается ls.out
как fd3
. Только для записи. Усекает (перезаписывает), если существует, иначе создает.
fcntl(1, F_DUPFD, 10) = 10
close(1) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
Это - немного манипулирования. Мы шунтируем STDOUT (fd1) прочь к fd10 и закрываем его. Это вызвано тем, что мы ничего не производим к реальному STDOUT с этой командой. Это заканчивается путем дублирования дескриптора записи к ls.out
и закрытие исходного.
stat("/opt/wine-staging/bin/ls", 0x7ffc6bf028c0) = -1 ENOENT (No such file or directory)
stat("/home/oli/bin/ls", 0x7ffc6bf028c0) = -1 ENOENT (No such file or directory)
stat("/usr/local/sbin/ls", 0x7ffc6bf028c0) = -1 ENOENT (No such file or directory)
stat("/usr/local/bin/ls", 0x7ffc6bf028c0) = -1 ENOENT (No such file or directory)
stat("/usr/sbin/ls", 0x7ffc6bf028c0) = -1 ENOENT (No such file or directory)
stat("/usr/bin/ls", 0x7ffc6bf028c0) = -1 ENOENT (No such file or directory)
stat("/sbin/ls", 0x7ffc6bf028c0) = -1 ENOENT (No such file or directory)
stat("/bin/ls", {st_mode=S_IFREG|0755, st_size=110080, ...}) = 0
Это - это ищущий исполняемый файл. Урок, возможно, чтобы не иметь длинный тракт ;)
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f0961324a10) = 31933
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 31933
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=31933, si_status=0, si_utime=0, si_stime=0} ---
rt_sigreturn() = 31933
dup2(10, 1) = 1
close(10) = 0
Затем выполнения команды и родитель ожидают. Во время этой операции любой STDOUT на самом деле отобразится на открытый дескриптор файла на ls.out
. Когда ребенок выходит SIGCHLD
, это говорит родительскому процессу его законченный и что он может возобновиться. Это разрушает с немного большим количеством манипулирования и завершением ls.out
.
Почему там так манипулирует? Нет я не совсем уверен также.
Конечно, можно изменить это поведение. Вы могли буферизовать к памяти wth что-то как sponge
и это будет невидимо от продолжающейся команды. Мы все еще влияем на дескрипторы файлов, но не видимым файловой системой способом.
ls | sponge ls.out