$ cat x
cat: x: No such file or directory
$ cat y
This is y.
$ cat x y 1> hold 2>&1
cat: x: No such file or directory
This is y.
Почему stder был перенаправлен для содержания также? stder был объявлен как stdout после перенаправления stdout в хранение и после произошедшего объявления больше нет перенаправления.
Базовая причина, почему то, потому что 1
больше не относится к stdout когда 2>&1
читается, но к hold
файл, так как перенаправления обрабатываются слева направо.
В первую очередь, помните, что все команды в среде Unix имеют стандартные потоки, на которые ссылаются через дескрипторы файлов 0 для stdin, 1 для stdout, и 2 для stderr. Существуют, конечно, редкие исключения, но 99% времен это - стандартные дескрипторы файлов.
Перенаправления такой как m>n
, m>&1
и m<n
выполните syscall dup2()
, который делает копии дескрипторов файлов (иначе дескрипторы файлов). В m>n
, m
обычно дескриптор файла, и n
может быть или файл или другой дескриптор файла. Это точно что 2>&1
- целочисленные ссылки на дескрипторы файлов, соответствующие stdin и stdout.
Когда cat x y 1> hold 2>&1
происходит, оболочка сначала откроется hold
файл и ссылка это через следующий доступный дескриптор файла, обычно 3
, и затем выполните копию того дескриптора файла через dup2(3,1)
. dup2()
syscall отчасти похож cp
команда, где Вы имеете cp old copy
. Таким образом, теперь дескриптор файла 1
обращается к тому же открытому описанию файла (иначе struct file
в ядре Linux) независимо от другого дескриптора файла 3.
Когда 2>&1
замечен, оболочка работает второй dup2(1,2)
. Таким образом, теперь дескриптор файла 2 является независимой копией дескриптора файла 1, но что это было прежде 2>&1
был замечен? 1
уже указывал на открытый файл hold
. Оттуда оболочка будет работать fork
и execve
syscall для фактического выполнения cat
как подпроцесс, который наследует открытые дескрипторы файлов.
Но насколько команды затронуты в этом случае cat
, это пишет в дескрипторы файлов 1 и 2, не будучи знающим, что они - копии чего-то еще.
Вы видите все это в действии с strace
команда:
# ... is several irrelevant lines skipped of bash opening libraries
$ strace -f -edup2,openat,write bash -c 'cat testFile.txt > hold 2>&1'
...
strace: Process 17766 attached
[pid 17766] openat(AT_FDCWD, "hold", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
[pid 17766] dup2(3, 1) = 1
[pid 17766] dup2(1, 2) = 2
[pid 17766] openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
[pid 17766] openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
[pid 17766] openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
[pid 17766] openat(AT_FDCWD, "testFile.txt", O_RDONLY) = 3
[pid 17766] write(1, "potato\n\nNAME MAJ:MIN RM SIZE"..., 248) = 248
[pid 17766] +++ exited with 0 +++
Если исходное намерение состоит в том, чтобы позволить stderr
обнаружьтесь на экране, затем 2>&1
может быть удален из команды. cat x y > hold
достаточно для отправки stdout в hold
файл и stderr для экранирования.
Если предназначение должно отправить stderr
через stdin
к каналу нам будет нужно к дескрипторам файла подкачки
$ cat x y 3>&2 2>&1 2>&3 2>hold | grep --color=always file
this is a test file y
$ cat hold
cat: x: No such file or directory
Это в основном выполняет подкачку как так:
# 3>&2 open new fd 3 , save copy of fd 2 there
dup2(2, 3) = 3
# 2>&1 , now turn 2 into copy of 1; 2 is still safe as fd 3
dup2(1, 2) = 2
# 2>&3 Now let's make 2 refer to what originally was 1, but now saved in 3
dup2(3, 2) = 2
# open file "hold" , which will be next available integer fd
openat(AT_FDCWD, "hold", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 4
# 2>hold
dup2(4, 2) = 2