Para esclarecer mais algumas coisas. Em:
echo foo >&3
echo
não está escrevendo "foo\n"
em seu descritor de arquivo 3. echo
está sempre gravando em stdout, em seu descritor de arquivo 1. Ele está chamando a chamada de sistema write()
com um 1
integer como primeiro argumento, um ponteiro para uma área na memória que começa com foo\n
como o segundo argumento e 4
(o comprimento de foo\n
) como o terceiro.
Em C, você escreveria write(1, "foo\n", 4)
. No código acima, o shell redireciona o fd 1 para a mesma descrição do arquivo aberto como aberto no fd 3 antes de chamar echo
(por meio da chamada de sistema dup2()
). Então, mesmo que seja funcionalmente equivalente, não é o mesmo que fazer write(3, "foo\n", 4)
. Na verdade, é algo como (simplificado):
if (pid = fork())
waitpid(pid, ...);
else {
dup2(3, 1);
execlp("echo", "foo", 0);
}
E echo
faz um write(1, "foo\n", 4)
Exceto que, em praticamente todos os shells, echo
está embutido, por isso não há fork
ou exec
. Em vez disso, o shell faz:
saved_stdout = dup(1);
dup2(3, 1);
builtin_echo("foo");
dup2(saved_stdout, 1); close(saved_stdout); /* restore stdout */
(onde builtin_echo()
é uma função que faz o write(1, "foo\n", 4)
no mesmo processo).
Para um comando que faz write(3, "foo\n", 4)
, você pode dar uma olhada no comando ksh
/ zsh
print -u3 foo
builtin.
Agora, todo processo é gratuito para usar os descritores de arquivos como desejarem. Exceto que 0, 1 e 2 são por convenção reservados para stdin, stdout e stderr. Outros fds geralmente não são especiais, mas em shells (que são apenas um tipo de aplicativo), fds 0 a <some-value>
em que algum valor é pelo menos 9 são reservados para uso pelo usuário (do shell). A concha não se misturará com a sua própria sopa interna. Por exemplo, meu saved_stdout = dup(1)
acima foi uma aproximação. Com efeito, o shell garantirá que saved_stdout
seja um valor maior que <some-value>
.
Agora, como não há nenhuma convenção anexada a fds fora de 0,1,2, você não pode esperar que algo esteja aberto no fd 3. O mais provável é que ele seja fechado. Ou se não for, é provável que o chamador do seu script tenha esquecido de fechá-lo (ou de adicionar o O_CLOEXC
flag a ele), pois não haveria motivo para deixá-lo aberto para você, pois ninguém espera que nada esteja aberto no fd 3.
Você usaria o fd 3 se soubesse que ele tinha sido aberto em algo, geralmente por você no mesmo script de antemão, como em:
{
var=$(cmd 2>&1 >&3)
} 3>&1
Onde o fd 3 foi definido como dup()
de fd 1 antes nós o usamos para cmd
to dup()
de volta para fd 1.