Чому $ ls > ls.out
причина "ls.out" буде включена до списку імен файлів у поточному каталозі? Чому саме цього обрали? Чому б інакше?
ls > ../ls.out
Чому $ ls > ls.out
причина "ls.out" буде включена до списку імен файлів у поточному каталозі? Чому саме цього обрали? Чому б інакше?
ls > ../ls.out
Відповіді:
При оцінці команди >
перенаправлення спочатку вирішується: так до моменту ls
запуску вихідний файл вже створений.
Це також причина, коли читання та запис у один і той же файл, використовуючи >
перенаправлення в межах однієї команди, обрізає файл; до моменту запуску команди файл усікається:
$ 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, програма / сценарій / все, що завгодно, не буде перенаправлено;
не заздалегідь перенаправляючи STDOUT, слід обов'язково зробити буфер оболонки вихідним програмою / сценарієм / будь-якими, поки STDOUT не буде перенаправлений;
Отже марна трата часу в першому випадку і марна трата часу і пам’яті у другому випадку.
Це саме те, що трапляється у мене, я не стверджую, що це фактичні причини; але я здогадуюсь, що взагалі, якби у когось був вибір, вони все-таки пішли б з перенаправленням все-таки з вищезазначених причин.
Від 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
.
Чому так багато жонглювання? Ні, я також не зовсім впевнений.
Звичайно, ви можете змінити таку поведінку. Ви можете створити буфер для пам’яті з чимось подібним, sponge
і це буде непомітно з команди, що йде. Ми все ще впливаємо на дескриптори файлів, але не так, як видима файлова система.
ls | sponge ls.out
Також є приємна стаття про реалізацію перенаправлення та операторів труб в оболонці . Що показує, як переадресація може бути реалізована, щоб це $ 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");
}
}