Por que os shells do UNIX são assim e como posso contornar isso?

2

Uma das coisas que notei sobre o BASH e outros shells do UNIX é que, por padrão e quando usados de maneira típica, eles geram subshells para quase tudo.

por exemplo.

foo=$(grep "someword" /path/to/somefile | awk '{print $3}')

geraria duas novas sessões de bash apenas para carregar algum texto em uma variável.

a) Por que as conchas fazem isso? Se um programa de linha de comando opera em descritores de arquivos, não é necessário gerar uma nova sessão de shell não interativa apenas para alimentá-lo com um descritor, certo?

b) Como posso trabalhar melhor com isso, ao carregar a saída do comando em variáveis? Eu sei que no bash você pode usar:

read -r -d '' < <(...)

para definir uma variável a partir de um comando sem usar um subshell, mas isso é um pouco complicado, e estou procurando métodos melhores (e mais portáveis). (Da mesma forma, seria legal se alguém soubesse alternativas gerais aos pipes e à substituição de comandos que não envolviam sub-unidades).

NB: Eu sei que "usuário Perl / Python / Ruby" é provavelmente a solução "correta", mas todos tendem a precisar de muito código padrão para fazer manipulação de arquivos, invocação de comandos externos, etc.

Edit: obrigado pelas respostas abaixo, no entanto, isso ainda não explica por que a substituição do processo deve bifurcar novos shells mesmo para comandos internos :

$ builtin echo $(builtin echo $(builtin echo $BASH_SUBSHELL))
2
    
por DanL4096 23.09.2014 / 18:05

2 respostas

3

Para executar um comando como grep ou awk , o shell deve bifurcar, o que significa que você obtém uma subcamada. As únicas exceções são quando o comando é embutido ou quando o comando é o último comando. Mas este último caso é apenas uma otimização feita por alguns shells e não pode ser feita sob algumas condições que podem alterar o comportamento (por exemplo, traps existentes); basicamente, isso é um exec implícito. Daí seus dois subshells para

foo=$(grep "someword" /path/to/somefile | awk '{print $3}')

Para evitar subshells, existem várias soluções alternativas e você precisa encontrá-las dependendo do contexto ...

    
por 23.09.2014 / 18:21
3

Focando em a):

Executando o comando

foo=$(echo bar)

inicia um subshell, porque o comando está usando uma substituição de comando, o que implica um subshell como o ambiente que é usado.

O comando executa explicitamente um subshell , simples assim.
E isso não muda se você aninhar.


Agora, depois da pergunta "Por que a casca está fazendo isso" é respondida:
Por que você está perguntando isso?

É um tópico interessante, mas não vejo totalmente o que você está mirando; Algumas suposições loucas:

  • Propondo uma implementação de otimização do bash?
  • Reduzindo a contagem de garfos? Por motivos de desempenho? Por algum tipo de razões estéticas? É a maneira mais rápida de fornecer um ambiente separado, incluindo espaço de endereço - um processo.
  • Alterando a sintaxe do shell? Para alcançar exatamente o objetivo?

Se você quiser, adicione detalhes à pergunta e deixe um comentário.


De man bash

COMMAND EXECUTION ENVIRONMENT
       [ ... ]

       Command substitution, commands grouped with  parentheses,  and  asyn‐
       chronous  commands  are  invoked  in a subshell environment that is a
       duplicate of the shell environment, except that traps caught  by  the
       shell  are reset to the values that the shell inherited from its par‐
       ent at invocation.  Builtin commands that are invoked as  part  of  a
       pipeline  are  also executed in a subshell environment.  Changes made
       to the subshell environment cannot affect the shell's execution envi‐
       ronment.

       [ ... ]
    
por 30.10.2014 / 00:56