Почему делает 'ls> ls.out потому что 'ls.out', который будет включен в список имен?

Почему делает $ ls > ls.out заставьте 'ls.out' быть включенным в список названий файлов в текущем каталоге? Почему это было выбрано, чтобы быть? Почему не иначе?

26
задан 1 February 2016 в 12:57

4 ответа

При оценке команды > перенаправление разрешено сначала: таким образом к этому времени 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 не перенаправляется;

Так пустая трата времени в первом случае и пустая трата времени и память во втором случае.

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

36
ответ дан 23 November 2019 в 01:07

Существует также хорошая статья приблизительно Реализация перенаправления и операторов канала в оболочке . То, которое показывает, как перенаправление могло быть реализовано так $ 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"); 
    }
}
6
ответ дан 23 November 2019 в 01:07

От 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, но их вывод может закончиться в файл, если файл не существует во-первых? Это похоже на воду. Можно ли получить стекло воды, не помещая стекло под краном сначала?

11
ответ дан 23 November 2019 в 01:07

Я не не соглашаюсь с текущими ответами. Выходной файл должен быть открыт, прежде чем выполнения команды или команда нигде не должны будут писать ее вывод.

Это вызвано тем, что "все - файл" в нашем мире. Выведенный на экран 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
10
ответ дан 23 November 2019 в 01:07

Другие вопросы по тегам:

Похожие вопросы: