strace
показывает эту последовательность системных вызовов:
$ strace -o strace.log tee /proc/self/fd/{3..6} 3>&1
...
$ cat strace.log
...
openat(AT_FDCWD, "/proc/self/fd/3", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 4
openat(AT_FDCWD, "/proc/self/fd/4", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 5
openat(AT_FDCWD, "/proc/self/fd/5", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 6
openat(AT_FDCWD, "/proc/self/fd/6", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 7
...
Первая строка открывает /proc/self/fd/3
и присваивает ей следующий доступный номер FD, 4. /proc/self/fd/3
- это специальный путь. Открытие его имеет эффект, подобный dup ing fd 3: fd 4 указывает на то же место, что и fd 3, tty.
То же самое происходит для каждого последующего вызова openat()
. Когда пыль оседает, все 4, 5, 6 и 7 являются дубликатами 3. 3. 1012 *
- 1 → tty
- 3 → tty
- 4 → tty
- 5 → tty
- 6 → tty
- 7 → tty
Обратите внимание, что перенаправление 3>&1
не важно. Важно то, что мы просим тройник открыть /proc/self/fd/N
, где N уже используется. Мы должны получить тот же результат, если избавимся от 3>&1
и получим тройниквместо этого начните с /proc/self/fd/2
. Давайте посмотрим:
$ echo foo | tee /proc/self/fd/{2..6}
foo
foo
foo
foo
foo
foo
Подтверждено! Тот же результат.
Мы можем повторять одно и то же число снова и снова. Мы получаем тот же результат, когда нажимаем fd 6. К тому времени, когда он достигает последнего, он открыл достаточно дескрипторов, чтобы сделать возможным переход к 6.
$ echo foo | tee /proc/self/fd/{2,2,2,2,6}
foo
foo
foo
foo
foo
foo