É> & - mais eficiente que> / dev / null?

58

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

Na verdade, esse comentário se refere ao guia do ABS como a fonte de suas informações. Mas essa fonte diz que a >&-sintaxe "fecha os descritores de arquivo".

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?

Parece que fechar um descritor é como fechar uma porta, mas redirecioná-lo para um dispositivo nulo está abrindo uma porta para o limbo! Os dois não me parecem exatamente iguais, porque se eu vir uma porta fechada, não tentarei jogar nada, mas se vir uma porta aberta, assumirei que posso.

Em outras palavras, sempre me perguntei se >/dev/nullmeios que cat mybigfile >/dev/nullrealmente processariam cada byte do arquivo e o gravariam no /dev/nullesquecimento. Por outro lado, se o shell encontrar um descritor de arquivo fechado, costumo pensar (mas não tenho certeza) que ele simplesmente não escreverá nada, embora a questão permaneça se catainda será lido todos os bytes.

Este comentário diz >&-e >/dev/null" deveria " ser o mesmo, mas não é uma resposta tão retumbante para mim. Gostaria de ter uma resposta mais autorizada com alguma referência ao núcleo padrão ou de origem ou não ...

jamadagni
fonte
Se eu quisesse ser caridoso, diria que o comentário não significava que eles foram implementados da mesma maneira, apenas que ambos têm o mesmo resultado final, impedindo que você veja a saída do programa.
Barmar 29/10

Respostas:

71

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

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

Por exemplo, se você fizer:

echo text | tee file >&-

Quando tee(pelo menos algumas implementações, como o busybox ') abrir o arquivo para gravação, ele será aberto no descritor de arquivo 1 (stdout). Então tee, escreveremos textduas 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)                           = ?

Sabe-se que isso causa vulnerabilidades de segurança. Por exemplo:

chsh 2>&-

E chsh(um aplicativo setuid) pode acabar escrevendo mensagens de erro no /etc/passwd.

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

A maioria das ferramentas, se não puderem gravar no stdout (porque, por exemplo, não está aberto), 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.

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

Stéphane Chazelas
fonte
4
Eu acho que essa resposta seria ainda melhor se o parágrafo final estivesse no topo (já que é o que mais responde diretamente à pergunta do OP), e depois passou a discutir por que é uma má ideia, mesmo que funcionasse principalmente. Mas eu aceito, tenho um voto positivo. ;)
um CVn
@ StéphaneChazelas: Como Michael disse, eu esperava o último parágrafo no topo, mas obrigado por esclarecer isso provavelmente só cria problemas. Então eu acho que uma pergunta complementar seria: quando o fechamento de um DF será realmente útil? Ou devo fazer isso como uma pergunta separada?
Jamadagni
@jamadagni, consulte unix.stackexchange.com/search?q=user%3A22565+%223%3E%26-%22 para alguns exemplos
Stéphane Chazelas
11
@jamadagni Se o link fornecido por Stéphane não responder à pergunta, eu diria que isso soa como o início de uma pergunta separada, pois não está diretamente relacionada à eficiência relativa dos dois métodos.
um CVn
11
Compreendo que Stephane comece com este importante aviso de segurança, pois seria menos visível se o último parágrafo estivesse no topo. +1 de mim.
Olivier Dulac 25/10
14

Eu sempre me perguntei se os >/dev/nullmeios que cat mybigfile >/dev/nullrealmente processariam cada byte do arquivo e o gravariam no /dev/nullesquecimento.

Não é uma resposta completa para sua pergunta, mas sim, o que foi dito acima é como funciona.

catlê o (s) arquivo (s) nomeado (s) ou a entrada padrão, se nenhum arquivo for nomeado, e envia para seu padrão a saída do conteúdo deles até encontrar um EOF no último arquivo nomeado (incluindo entrada padrão). Esse é o seu trabalho.

Ao adicionar, >/dev/nullvocê está redirecionando a saída padrão para / dev / null. Esse é um arquivo especial (um nó do dispositivo) que joga fora qualquer coisa escrita nele (e retorna imediatamente o EOF na leitura). Observe 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 no nome / dev / null, apenas o que existe lá na maioria dos sistemas semelhantes ao Unix .

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

Segue-se que catnão tem conhecimento das propriedades especiais de / dev / null e, de fato, provavelmente não tem conhecimento do redirecionamento, mas ainda precisa executar exatamente o mesmo trabalho: lê os arquivos nomeados e gera o conteúdo de o (s) arquivo (s) para sua saída padrão. catAcontece que o resultado padrão de ir para o vazio não é algo em catsi.

um CVn
fonte
2
Para estender sua resposta: Sim, cat mybigfile > /dev/nullfará com catque cada byte seja lido bigfilena memória. E, para todos os nbytes, ele chamará write(1, buffer, n). Sem o conhecimento do catprograma, o programa writenão fará absolutamente nada (exceto, talvez, por alguma contabilidade trivial). Escrever para /dev/nullnão requer processamento de todos os bytes.
G-Man diz 'Reinstate Monica'
2
Lembro que fiquei impressionado ao ler a fonte do kernel Linux do dispositivo / dev / null. Eu esperava que houvesse algum sistema elaborado de buffers livres (), etc., mas, não, é basicamente um retorno ().
Brian Minton 24/10
4
@ G-Man: Eu não sei se você pode garantir que isso seja verdade em todos os casos. Não consigo encontrar evidências agora, mas lembro-me de que alguma implementação de um catou de cpoutro funcionaria mmapinserindo grandes pedaços do arquivo de origem na memória e depois chamando write()a região mapeada. Se você estivesse escrevendo /dev/null, a write()chamada retornaria imediatamente, sem falhas nas páginas do arquivo de origem, para que nunca fosse realmente lida no disco.
Node Eldredge 24/10
2
Além disso, algo como o GNU caté executado em muitas plataformas, mas uma olhada casual no código-fonte mostrará muitos #ifdefs: não é literalmente o mesmo código que é executado em todas as plataformas, e há muitas seções dependentes do sistema.
Nate Eldredge 24/10
@NateEldredge: Ponto interessante, mas eu estava apenas aproveitando a resposta de Michael, então você não está me contradizendo tanto quanto está contradizendo Michael.
G-Man diz 'Reinstate Monica'