É permitido ao shell otimizar comandos de terminação inúteis?

27

Se for solicitado a um shell que execute um comando provavelmente inútil ( ou parcialmente inútil ) conhecido por terminar, como, por exemplo cat hugeregularfile.txt > /dev/null, ele pode pular a execução desse comando ( ou executar um equivalente mais barato, por exemplo touch -a hugeregularfile.txt )?

De maneira mais geral, o shell é semelhante aos compiladores C, pois pode executar qualquer transformação no código-fonte, desde que o comportamento observável externamente seja como se a máquina abstrata o avaliasse?

EDITAR

Nota Bene: Minha pergunta, como originalmente colocada, tinha um título que perguntava se o shell tem permissão para fazer essas otimizações, não se deveria ou mesmo se existem implementações que possam fazê-las. Estou mais interessado na teoria do que na prática, embora ambas sejam bem-vindas.

Não existirá idonotexist
fonte
Não, o shell não é tão inteligente quanto os compiladores modernos. De fato, é um tanto idiota. Não otimizaria nenhum código inútil.
devnull
12
Adivinhar qual é a intenção do usuário não é algo que o shell deva fazer. O usuário pode estar tentando fazer quase qualquer coisa com esse comando, otimizá-lo seria a coisa errada a fazer, mesmo que fosse possível.
Chris Baixo
11
Não importa dizer que, se o arquivo era um dispositivo, catting faz uma grande diferença. O shell pode saber que o arquivo é um dispositivo, mas não precisa ser confiável.
yo
3
Os compiladores @StephaneChazelas C não precisam "pedir permissão de alguém" para otimizar seus programas compilados; Existe uma regra como se no padrão C que permite que eles façam isso. O padrão POSIX parece ter padronizado pelo menos um shell ( pubs.opengroup.org/onlinepubs/009695399/utilities/… ), além de vários outros utilitários ( pubs.opengroup.org/onlinepubs/009604499/utilities/wc.html para wc, por exemplo). Mas, até onde sei, o POSIX não se posiciona sobre a otimização de shell; Ou faz?
Iwillnotexist Idonotexist
2
A otimização está melhorando o desempenho com atalhos sem afetar a funcionalidade. Enquanto a funcionalidade estiver garantida, não consigo ver a objeção POSIX. Porém, sua otimização proposta quebraria as especificações do gato . Existem formulações específicas na especificação POSIX que existem para acomodar o tipo de otimização feito por ksh. Como se não dissessem processo separado, mas ambiente de subcamação para permitir otimizações de economia de forquilha.
Stéphane Chazelas

Respostas:

26

Não, isso seria uma má ideia.

cat hugeregularfile.txt > /dev/nulle touch -a hugeregularfile.txtnão são os mesmos. catlerá o arquivo inteiro, mesmo se você redirecionar a saída para /dev/null. E ler o arquivo inteiro pode ser exatamente o que você deseja. Por exemplo, para armazená-lo em cache, para que as leituras posteriores sejam significativamente mais rápidas. A concha não pode conhecer sua intenção.

Da mesma forma, um compilador C nunca otimizará a leitura de um arquivo, mesmo que você não veja o que lê.

scai
fonte
2
@ Willnotexist: Todo comando útil (exceto indiscutivelmente truee false) tem efeitos colaterais em potencial, e os efeitos colaterais são quase sempre o ponto de chamar o comando. O shell não podia conhecer esses efeitos colaterais com antecedência (para programas externos cat) sem resolver o problema da parada. Portanto, com razão, não tente e assume que você quis dizer o que disse.
cHao 10/03
5
@IwillnotexistIdonotexist Não, o shell não pode ver tudo o que está prestes a acontecer. Não tem idéia cat. De fato, você catpode fazer qualquer coisa, desde a formatação do disco rígido até o download da Internet.
Scal
5
"O Unix não foi projetado para impedir seus usuários de fazerem coisas estúpidas, pois isso também os impediria de fazer coisas inteligentes". - Doug Gwyn
Agi Hammerthief
7
@cHao E mesmo truee falsedefinido $?.
Kyle Strand
3
Como o @scai apontou acima, os executáveis ​​não são como palavras-chave de idioma: cate /dev/nulltêm significados típicos, mas não garantem que se comportem dessa maneira. Para realizar otimizações e garantir nenhuma alteração no comportamento esperado, a otimização só pode ser permitida envolvendo construções implementadas no próprio shell e não coisas encontradas no ambiente de execução ... não importa o quão intuitivo possa ser o nome deles.
21814 Andybuckley
20

Não, já que /dev/nullé apenas um nome, que pode ser usado para qualquer outro dispositivo ou arquivo que não seja o que "normalmente" é um coletor de dados.

Portanto, um shell (ou qualquer outro programa) não tem idéia, com base no nome, se o arquivo para o qual está gravando está fazendo algo "de verdade" com os dados. Também existem AFAIK que nenhum sistema chama pelo programa shell, para determinar que, por exemplo, o descritor de arquivo não está realmente fazendo nada.

Sua comparação com a otimização de código ausente em um programa C não funciona, pois um shell não possui a visão geral total que um compilador C possui sobre um pedaço de código-fonte. Um shell não sabe o suficiente sobre /dev/nullcomo otimizar seu exemplo, mais como um compilador C não sabe o suficiente sobre o código em uma chamada de função à qual ele se vincula dinamicamente, para não fazer a chamada.

Anthon
fonte
4
Como se vê, o ksh93 tratará /dev/nullespecialmente, às vezes. Um built-in que tem seu stdout direcionado para /dev/null, por exemplo echo foo >/dev/null, não resultará em gravações /dev/null. Não faz nada de especial se estiver invocando um comando não interno (como cat file >/dev/null).
Mark Plotnick
De fato, cattambém poderia ser outra coisa. Qualquer outra coisa de fato.
orion
3
Na verdade, /dev/nullé uma das poucas caminhos normalizados , juntamente com /dev/tty, /dev/console, /tmp, /dev/e /.
Gilles 'SO- stop be evil'
2
@MarkPlotnick Na verdade, cat é um ksh93 embutido (não ativado, a menos que você o coloque /opt/ast/binantes /bin(ou onde houver cat) $PATH). E sim, embora cat file > /dev/nullcom esse built-in faça reado conteúdo de file, ele não o grava em / dev / null (embora o abra e faça o fstats).
Stéphane Chazelas
14

Ele não otimiza os comandos em execução (e você já recebeu várias respostas excelentes, informando por que não deveria), mas pode otimizar os garfos, pares de tubos / soquetes e leituras em alguns casos. O tipo de otimizações que ele pode fazer:

  • Com a maioria dos shells modernos, o último comando em um script geralmente será executado no processo do shell, a menos que alguns traps tenham sido definidos. Por exemplo, em sh -c ls, a maioria das shimplementações ( bash, mksh, ksh, zsh, yash, algumas versões do ash) não vai desembolsar um processo a ser executado ls.
  • em ksh93, a substituição de comando não criará um processo de tubulação ou bifurcação até que um comando externo seja chamado ( $(echo foo)por exemplo, será expandido para foosem um tubo / par de soquete ou forquilha).
  • o readbuilt-in de alguns shells ( bash, AT&T ksh) não fará leituras de byte único se eles detectarem que stdin é procurável (nesse caso, eles farão leituras grandes e buscarão o final do que devem ler).
Stéphane Chazelas
fonte
Eu gosto desta resposta, mas não está claro se esta é a razão original, ou se a informação é retirada de alguma referência (que eu gostaria de aprofundar)
yoniLavi
3
@yoniYalovitsky, essa é a razão original . ksh93é o shell que lidera o caminho da frente de otimização, pois seu objetivo é / deveria ser visto como compatível com linguagens de programação como perl. Assim, você pode consultar a kshdocumentação, o código (boa sorte) e a lista de discussão para obter mais informações.
Stéphane Chazelas
11
@HenkLangeveld, sim, é possível verificar que, com sh -c 'ps -p "$$"'o que lhe dará pse não shcom essas shimplementações, ou com strace / pequena / Tusc ...
Stéphane Chazelas
11
A diferença entre ksh -c 'ps; ps'e bash -c 'ps; ps' é interessante. O Ksh93 vai mais longe na sua otimização.
Henk Langeveld
11
@ HenkLangeveld, depende de qual implementação kshestamos falando aqui. mkshse comporta como bash. Esse comportamento é principalmente destinado a otimizar coisas como system("some command"). Observe que há um efeito colateral dessa otimização quando se trata do status de saída dos processos terminados por um sinal (em alguns shells). ksh93costumava ter um bug , pois fazia a otimização mesmo quando as armadilhas eram definidas.
Stéphane Chazelas
7

Ao ver cat hugeregularfile.txt > /dev/null, a concha não pode acreditar que a ação é inútil - catnão faz parte da concha e poderia fazer qualquer coisa na teoria e também na prática.

Por exemplo, o usuário pode ter renomeado o executável rmpara cate, de repente, a linha executa um comportamento observável externamente, ou seja, removendo o arquivo.

O usuário pode ter compilado uma versão catque entra em um loop infinito; portanto, o shell não pode assumir que ele é conhecido por terminar, como você sugere.

Alguém pode ter instalado uma versão catque funcione conforme o planejado, mas com um efeito colateral extra de instalar um rootkit, se ele for executado com privilégios adequados - novamente, o shell deve executá-lo devidamente.

Peter é
fonte
2
Na verdade, mkshele realmente otimiza V=$(cat file), tornando-o um builtin. Portanto, o shell pode otimizá-lo, mas não transformá- lo em apenas a touch -a.
Steve Schnepp
11
@SteveSchnepp, cat é um embutido no mksh, mas que resorts embutidas para o sistema está catse aprovada qualquer opção, e é por isso com GNU cat, mksh -c 'cat /dev/null --help'não produz o mesmo resultado que bash -c 'cat /dev/null --help', mas mksh -c 'cat --help /dev/null'lhe dá o mesmo que bash -c 'cat --help /dev/null'(como mkshgato builtin Analisa as opções POSIX enquanto o GNU cat os analisa da maneira GNU).
Stéphane Chazelas
No bash e no ksh93, o V=$(cat file)pode ser otimizado com V=$(< file). Isso acelera as coisas, mesmo sem um builtin cat.
Henk Langeveld 14/03