Почему «ls & gt; ls.out 'потому что' ls.out 'должен быть включен в список имен?

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

1
задан 1 February 2016 в 23:57

3 ответа

Из man bash:

REDIRECTION Перед выполнением команды его ввод и вывод могут быть перенаправлены с использованием специальной нотации, интерпретируемой оболочкой. Перенаправление позволяет дублировать, открывать, закрывать файлы команд, ссылаться на разные файлы и изменять файлы, с которых команда считывает и записывает.

Первое предложение предполагает, что вывод выполняется для перехода куда-то, кроме 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 May 2018 в 13:50

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

Это потому, что «все есть файл» в нашем мире. Выход на экран - SDOUT (также известный как дескриптор файла 1). Для приложения для записи на терминал он открывает fd1 и записывает его как файл.

Когда вы перенаправляете вывод приложения в оболочку, вы изменяете fd1, чтобы он фактически указывал на файл.

Но вам все это приятно сказать, но вы можете легко посмотреть, как это работает с 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, это говорит о завершении родительского процесса и возобновлении его.

Почему открывает много жонглирования? Нет, я тоже не совсем уверен.

Конечно, вы можете изменить это поведение. Вы можете буферизировать в память с чем-то вроде sponge, и это будет невидимым из текущей команды. Мы по-прежнему влияем на дескрипторы файлов, но не на вид файловой системы.

ls | sponge ls.out
10
ответ дан 23 May 2018 в 13:50

Также есть хорошая статья о реализации перенаправления и операторов труб в оболочке. Что показывает, как перенаправление может быть реализовано, так что $ 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 May 2018 в 13:50

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

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