É & - mais eficiente que / dev / null?

59

Ontem eu li este SO comentário que diz que no shell (pelo menos bash ) >&- "tem o mesmo resultado que" >/dev/null .

Esse comentário realmente se refere ao guia do ABS como a fonte de suas informações. Mas essa fonte diz que a sintaxe >&- "fecha os descritores de arquivos".

Não está claro para mim se as duas ações de fechar um descritor de arquivo e redirecioná-lo para o dispositivo nulo são totalmente equivalentes. Então, minha pergunta é: eles são?

Na superfície, parece que fechar um descritor é como fechar uma porta, mas redirecioná-la para um dispositivo nulo é abrir uma porta para o limbo! Os dois não parecem exatamente o mesmo para mim, porque se eu vejo uma porta fechada, não vou tentar jogar nada fora disso, mas se eu ver uma porta aberta, vou assumir que posso.

Em outras palavras, sempre me perguntei se >/dev/null significa que cat mybigfile >/dev/null processaria cada byte do arquivo e o gravaria em /dev/null , que o esquece. Por outro lado, se o shell encontrar um descritor de arquivo fechado, tenho a tendência de pensar (mas não tenho certeza) de que ele simplesmente não escreverá nada, embora permaneça a pergunta se cat ainda ler a cada byte.

This comentar diz que >&- e >/dev/null " devem " ser o mesmo, mas não é uma resposta tão retumbante para mim. Eu gostaria de ter uma resposta mais autoritária com alguma referência ao núcleo padrão ou fonte ou não ...

    
por jamadagni 24.10.2014 / 10:46

2 respostas

74

Não, você certamente não quer fechar os descritores de arquivo 0, 1 e 2.

Se você fizer isso, na primeira vez que o aplicativo abrir um arquivo, ele se tornará stdin / stdout / stderr ...

Por exemplo, se você fizer isso:

echo text | tee file >&-

Quando tee (pelo menos algumas implementações, como busybox ') abre o arquivo para gravação, ele será aberto no descritor de arquivo 1 (stdout). Então tee irá escrever text duas vezes em file :

$ echo text | strace tee file >&-
[...]
open("file", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 1
read(0, "text\n", 8193)                 = 5
write(1, "text\n", 5)                   = 5
write(1, "text\n", 5)                   = 5
read(0, "", 8193)                       = 0
exit_group(0)                           = ?

Isso é conhecido por causar vulnerabilidades de segurança. Por exemplo:

chsh 2>&-

E chsh (uma aplicação setuid) pode acabar escrevendo mensagens de erro em /etc/passwd .

Algumas ferramentas e até algumas bibliotecas tentam se proteger contra isso. Por exemplo, o GNU tee irá mover o descritor de arquivo para um acima de 2 se os arquivos que ele abrir para gravação forem atribuídos 0, 1, 2 enquanto busybox tee não será.

A maioria das ferramentas, se não puderem gravar no stdout (porque, por exemplo, não está aberta), reportará uma mensagem de erro no stderr (no idioma do usuário, o que significa processamento extra para abrir e analisar arquivos de localização ... ), portanto, será significativamente menos eficiente e possivelmente causará falha no programa.

Em qualquer caso, não será mais eficiente. O programa ainda fará uma chamada de sistema write() . Ele só pode ser mais eficiente se o programa desistir de gravar em stdout / stderr após a primeira falha na chamada do sistema write() , mas os programas geralmente não fazem isso. Eles geralmente saem com um erro ou continuam tentando.

    
por 24.10.2014 / 11:08
14

IOW I have always wondered if >/dev/null means that cat mybigfile >/dev/null would actually process every byte of the file and write it to /dev/null which forgets it.

Não é uma resposta completa para a sua pergunta, mas sim, o que está acima é como funciona.

cat lê o (s) arquivo (s) nomeado (s), ou entrada padrão se nenhum arquivo é nomeado, e envia para sua saída padrão o conteúdo daqueles até encontrar um EOF ativado (incluindo entradas padrão ) o último arquivo chamado. Esse é o seu trabalho .

Ao adicionar >/dev/null você está redirecionando a saída padrão para / dev / null. Esse é um arquivo especial (um nó de dispositivo) que elimina qualquer coisa escrita nele (e imediatamente retorna EOF na leitura). Note que o redirecionamento de E / S é um recurso fornecido pelo shell, não por cada aplicativo individual, e que não há nada mágico sobre o nome / dev / null, somente o que acontece lá na maioria < a href="https://en.wikipedia.org/wiki/Unix-like"> Sistemas semelhantes ao Unix .

Também é importante observar que a mecânica específica dos nós de dispositivos varia de sistema operacional a sistema operacional, mas cat (que, em um sistema GNU, significa coreutils) é multi-plataforma (o mesmo código-fonte precisa ser executado) > pelo menos no Linux e Hurd) e, portanto, não pode ter dependências para kernels específicos do sistema operacional. Além disso, ainda funciona se você criar um alias / dev / null (no Linux, isso significa um nó de dispositivo com o mesmo número de dispositivo maior / menor) com outro nome. E sempre há o caso de escrever em outro lugar que se comporte efetivamente da mesma forma (digamos, / dev / zero).

Conclui-se que cat não tem conhecimento das propriedades especiais de / dev / null e, na verdade, provavelmente não está ciente do redirecionamento, mas ainda precisa executar exatamente o mesmo trabalho: lê os arquivos nomeados e envia o conteúdo do (s) arquivo (s) para sua saída padrão. Que a saída padrão de cat passe para um vazio não é algo com que cat se preocupa.

    
por 24.10.2014 / 11:04