Qual é a diferença entre o redirecionamento "& 1" e "/ proc / self / fd / 1"?

6

Estou trabalhando em algum script que está sendo executado por rc.local na inicialização, e notei que o redirecionamento de saída funciona muito estranho.

Se eu escrever algo como echo "foo" >&1 , ele acaba no syslog, e tudo está bem.

Mas quando escrevo echo "foo" >>/dev/stdout ou echo "foo" >>/proc/self/fd/1 , recebi um erro dizendo que não há tal dispositivo ou endereço.

Investigações posteriores revelaram que /proc/self/fd/1 era de fato um soquete. A inclusão de ls -l /proc/self/fd em rc.local imprime o seguinte:

lr-x------. 1 root root 64 Jul 14 05:28 0 -> /dev/null
lrwx------. 1 root root 64 Jul 14 05:28 1 -> socket:[18485]
lrwx------. 1 root root 64 Jul 14 05:28 2 -> socket:[18485]
lr-x------. 1 root root 64 Jul 14 05:28 255 -> /etc/rc.d/rc.local

O comportamento descrito pode ser facilmente reproduzido com nc . Primeiro, descubra qual descritor de arquivo se vincula ao soquete. Você pode fazer isso emitindo nc -l -p 25566 -c "ls -l /proc/self/fd" no primeiro terminal e, em seguida, telnet localhost 25566 no outro terminal. No meu caso, foi o 5º descritor.

Ok. Então, para reproduzir o problema, no primeiro terminal:

nc -l -p 25566 -c "echo 'hello' >&5"

no segundo terminal:

telnet localhost 25566

Você verá "hello" na segunda saída do terminal entre as mensagens telnet sobre a conexão sendo estabelecida e fechada.

Agora, o caso com o pseudo arquivo de descritor de arquivo de /proc :

primeiro terminal:

nc -l -p 25566 -c "echo 'hello' >/proc/self/fd/5"

segundo terminal:

telnet localhost 25566

Agora, o segundo terminal contém apenas mensagens telnet sobre a conexão estabelecida e imediatamente fechada, e o primeiro terminal mostra um erro: sh: /proc/self/fd/5: No such device or address .

Editar: o SO é o servidor Fedora 23 i686

Então, a questão. Qual é a diferença entre >&1 e >/proc/self/fd/1 ? E existe alguma maneira universal e confiável de redirecionar a saída para algum arquivo, o que corresponde exatamente à saída padrão atual?

Obrigado.

Editar 2:

Para maior clareza, entrada / saída exata para o Fedora 23 i686 para casos acima:

Terminal 1                                       | Terminal 2
$ nc -l -p 25566 -c 'ls -go /proc/self/fd'       | 
                                                 | $ telnet localhost 25566
                                                 | Trying ::1...
                                                 | Connected to localhost.
                                                 | Escape character is '^]'.
                                                 | total 0
                                                 | lr-x------ 1 64 Jul 14 08:56 0 -> pipe:[19687]
                                                 | l-wx------ 1 64 Jul 14 08:56 1 -> pipe:[19688]
                                                 | lrwx------ 1 64 Jul 14 08:56 2 -> /dev/tty2
                                                 | lr-x------ 1 64 Jul 14 08:56 3 -> pipe:[19687]
                                                 | lr-x------ 1 64 Jul 14 08:56 4 -> /proc/1285/fd
                                                 | lrwx------ 1 64 Jul 14 08:56 5 -> socket:[19686]
                                                 | l-wx------ 1 64 Jul 14 08:56 7 -> /pipe:[19688]
                                                 | Connection closed by foreign host.
$ nc -l -p 25566 -c "echo 'hi' >&5"              | 
                                                 | $ telnet localhost 25566
                                                 | Trying ::1...
                                                 | Connected to localhost.
                                                 | Escape character is '^]'.
                                                 | hi
                                                 | Connection closed by foreign host.
$ nc -l -p 25566 -c "echo 'hi' >/proc/self/fd/5" | 
                                                 | $ telnet localhost 25566
                                                 | Trying ::1...
                                                 | Connected to localhost.
sh: /proc/self/fd/5: No such device or address   | Escape character is '^]'.
                                                 | Connection closed by foreign host.
$                                                | $
    
por alexey.e.egorov 14.07.2016 / 16:18

2 respostas

6

Use >&N . É portátil e, como você viu, funciona com soquetes.

A única razão pela qual você usaria /proc/self/fd é que você está executando um programa que espera um nome de arquivo e não pode ser instruído a usar um descritor de arquivo já aberto. Por exemplo. o redirecionamento <(cmd...) usa isso, já que quase todos os utilitários de linha de comando podem abrir um arquivo apontado pelo nome, mas nem todos os descritores de arquivos pré-abertos suportam diretamente.

Seu shell pode usar descritores de arquivos pré-existentes, portanto, não é necessário passar por /proc .

Além disso, /proc/NNN/fd/ é específico do Linux e requer que você tenha um /proc montado. Nas minhas caixas de Linux, /dev/stdout , /dev/fd/* e outros são links simbólicos para /proc/self/fd/* etc., então eles exigem /proc também. Em outros Unixes, eles podem ser diferentes. De acordo com as respostas para uma pergunta antiga /dev/stdout estão especificamente listadas como fora do POSIX.

Quanto ao motivo pelo qual o redirecionamento não funciona como você tentou: Experimentando com strace , a diferença entre os dois é que para um redirecionamento >&N , bash chama dup() no descritor de arquivo e para >/proc/self/fd/N ele apenas tenta abri-lo como um arquivo comum com open() . Aparentemente proc não suporta a abertura de novas cópias de sockets assim, então a chamada falha. Os sockets de stream são basicamente links ponto a ponto, então proibir a abertura de uma nova cópia não parece muito antinatural. Mas por que funciona para pipes ou com dup , não sei dizer.

$ ls -l /proc/self/fd/3
lrwx------ 1 itvirta itvirta 64 Jul 14 18:24 /proc/self/fd/3 -> socket:[168157]
$ strace bash -c "echo foo >>/proc/self/fd/3" 2>&1 | grep open.*proc/self
open("/proc/self/fd/3", O_WRONLY|O_CREAT|O_APPEND, 0666) = -1 ENXIO (No such device or address)

Também esta resposta tem algumas informações sobre a portabilidade de /proc/NNN/fd

    
por 14.07.2016 / 17:38
2

Um redirecionamento como 3>&1 duplica um descritor de arquivo existente : isso leva o mesmo arquivo aberto (mesmo arquivo, mesmo flags, mesma posição, etc.) e conecta-se a outra “porta de saída” do programa (outro número descritor de arquivo). (Mais precisamente, isso cria um novo descritor de arquivo que aponta para a mesma descrição do arquivo , mas nós não o fazemos)

>&1 duplica o descritor para si mesmo, alguns shells o otimizam completamente.

Um redirecionamento como >/proc/$pid/fd/1 abre o arquivo /proc/$pid/fd/1 . Isso cria um descritor de arquivo novo. Os arquivos em /proc/*/fd são especiais e, ao abri-los, copia principalmente os dados de um descritor de arquivo existente. Embora os arquivos sejam links simbólicos, eles são “mágicos”; por exemplo, um arquivo excluído ou um pipe aparece como um link simbólico quebrado, mas ainda pode ser aberto como se fosse um arquivo existente, pois o kernel contém código especial para manipular /proc/*/fd entradas. Portanto, na maioria dos casos, >&1 e >/proc/self/fd/1 são equivalentes. No entanto, os soquetes recebem tratamento diferente .

/*
 *      In theory you can't get an open on this inode, but /proc provides
 *      a back door. Remember to keep it shut otherwise you'll let the
 *      creepy crawlies in.
 */

static int sock_no_open(struct inode *irrelevant, struct file *dontcare)
{
        return -ENXIO;
}

(O código foi reorganizado em versões recentes, mas os soquetes ainda não podem ser abertos mesmo via /proc/PID/fd/NUM .)

O motivo pelo qual você não pode abrir um soquete como a maioria dos outros tipos de arquivos é que há mais informações anexadas a um soquete, você precisa dizer como abri-lo. Por exemplo, a abertura de um soquete TCP aloca uma porta de origem. Embora possa fazer sentido em alguns contextos, o kernel do Linux não permite abrir os sockets de outro processo. No caso em que o processo é o mesmo ( /proc/self/fd/NUM , em oposição a /proc/OTHERPID/fd/NUM ), ele poderia converter a chamada open para a duplicação de descritor de arquivo usual, mas abrir /proc/self/fd é uma coisa incomum na o primeiro lugar, e o redirecionamento com sockets não é feito normalmente, já que normalmente não funciona, então o kernel não foi projetado para suportar esta exceção sensata, mas inútil.

    
por 15.07.2016 / 04:12