2015-11-06 6 views
4

Я нашел странное и совершенно неожиданное поведение при работе с перенаправлением в bash, и даже если мне удастся обойти его, я хотел бы знать, почему это происходит.Неожиданное поведение в переназначении bash

Если я запустил эту команду: { echo wtf > /dev/stdout ; } >> wtf.txt N раз, я ожидаю увидеть заполненные строки N "wtf". То, что я нашел в файле, - это одна строка.

Я думаю, что поскольку первая команда открывает/dev/stdout в режиме усечения, тогда режим наследуется вторым файловым дескриптором (wtf.txt), который затем полностью удаляется, но я хотел бы знать если некоторые из вас могут объяснить это лучше, и если это правильное поведение или ошибка.

Просто, чтобы быть ясным, команда, которую я использовал, была другой, но пример echo проще понять. Первоначальная команда была командой, которой нужен выходной файл в качестве аргумента, и поскольку я хочу, чтобы вывод на stdout прошел в качестве аргумента /dev/stdout. Такое же поведение можно проверить с помощью команды openssl rand -hex 4 -out /dev/stdout >> wtf.txt.

Наконец, решение мне удалось решить проблему делегирования операции добавления к тройнику следующим образом: { echo wtf > /dev/stdout } | tee -a wtf.txt > /dev/null

+1

Это происходит потому, что сначала выполняется перенаправление '>> wtf.txt', а затем, когда обрабатывается'>/dev/stdout', вы больше не добавляете в stdout из-за '>' вместо '> > '. Итак, он работает: '{echo wtf >>/dev/stdout; } >> wtf.txt' – whoan

+0

Думайте, что вы делаете что-то вроде: 'echo wtf >> wtf.txt> wtf.txt'. С '>> wtf.txt' вы указали'/dev/stdout' * * на 'wtf.txt'. Затем, используя '>/dev/stdout', вы заменяете предыдущее перенаправление. '/ dev/stdout', на данный момент, является символической ссылкой на' wtf.txt'. – whoan

+0

Невозможно воспроизвести либо в bash 3.2, либо в bash 4.3, либо выполнив каждую из N команд вручную или быстро в цикле. – chepner

ответ

1

Вы можете проверить, что происходит с помощью Трассирования:

strace -o wtf-trace.txt -ff bash -c '{ (echo wtf) > /dev/stdout; } >> wtf.txt' 

Это будет генерировать два файла, такие как wtf-trace.txt.12889 и wtf-trace.txt.12890 в моем случае. Что происходит, процесс 1 >> wtf.txt:

open("wtf.txt", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3 
dup2(3, 1)        = 1 
close(3)        = 0 
clone(child_stack=0, .................) = 12890 
wait4(-1, [{WIFEXITED(s) .............) = 12890 
exit_group(0)       = ? 

Первый процесс открывает или создает «wtf.txt» для добавления и получить FD 3. После того, что он дублирует FD 1 с FD 3 и закрывает ФО 3. На этом этапе он вилки (клон), ждет его выхода и выхода.

Второй процесс { echo wtf > /dev/stdout } наследует файл, FD 1 (STDOUT) и делает:

open("/dev/stdout", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3 
dup2(3, 1)        = 1 
close(3)        = 0 
fstat(1, {st_mode=S_IFREG|0664, st_size=0, ...}) = 0 
write(1, "wtf\n", 4)     = 4 
exit_group(0)       = ? 

Как вы можете видеть, что это открывает /dev/stdout (обратите внимание O_TRUNC) и получает FD 3, dup2 получить FD 3 к FD 1, закрывает FD 3, проверяет FD 1 и получает файл с размером 0 st_size=0, пишет ему и выходит.

Если вы | cat >> то второй процесс получает его FD 1 соединен с трубой, которая не искать, может или укоротить-состояние ...

NB: Я показываю только соответствующие строки трассирования файлов генерироваться.