Compare a saída do comando dentro da instrução if sem subshell

3

De acordo com o link

Placing a list of commands between curly braces causes the list to be executed in the current shell context.

Mas quando eu tento isso: if [[ { type -t echo; } = "builtin" ]]; then echo 1; else echo 0; fi recebo o seguinte erro:

-bash: conditional binary operator expected

-bash: syntax error near 'type'

Tudo bem, eu entendo que não é para ser usado assim. Agora eu sei que, se eu usar if [[ $( type -t echo ) = "builtin" ]]; then echo 1; else echo 0; fi , teoricamente, vou conseguir a funcionalidade desejada.

Eu encontrei outras questões sobre como evitar o uso desnecessário de subshells respondidas, mas nenhuma delas explicou a comparação da saída de um comando sem um subshell. Tudo o que eu realmente quero é evitar o uso de um subshell onde ele não é realmente necessário e eu realmente gostaria que, se possível, executássemos essa verificação no contexto atual do shell - realmente parece que é assim que deve ser feito. P.S. Não me importo de usar variáveis e manipulação, se não houver outro jeito.

    
por G G 15.03.2018 / 22:27

1 resposta

4

Para obter a saída de um comando, você precisa lê-lo de alguma forma. type escreve em sua stdout. E precisamos de alguma forma obter isso e passá-lo para o comando [ .

$(...) usa um pipe para isso. Mas, para um pipe, você precisa de um escritor e um processo de leitura, então você terá que desembolsar um processo mesmo para executar um comando que esteja embutido. Você pode tentar ler e gravar em um pipe no mesmo processo, mas isso geralmente é propenso a deadlocks, pois a gravação pode ser bloqueada se ninguém estiver lendo (embora seja necessário que o buffer do pipe fique cheio para que isso aconteça, o que é improvável para type ) .

Você poderia fazer isso com o shell yash que tem uma interface bruta para pipe() :

{
  type echo >&3
  echo 3>&- # close the writing end so the reader can see an eof
  IFS= read -r answer <&4
} 3>>|4

Acima, você teria um deadlock se a saída de type fosse maior que o tamanho do buffer de pipe (64KiB por padrão nas versões modernas do Linux).

Com bash , você sempre pode fazer:

type -t echo > file
IFS= read -rd '' type < file
if [ "$type" = builtin ]...

Mas enquanto isso evita o subshell, isso significa jogar lixo no sistema de arquivos com esse file .

Aqui type é um incorporado. Sua saída é gerada pelo shell e é verdade que parece um pouco bobo ter que separar um processo para poder usar essa saída no shell.

Alguns shells ( ksh93 e fish ) implementam algumas otimizações lá. Em seu $(type echo) ( (type echo) in fish ), eles na verdade falsificam a escrita da saída e a leitura dela. Quando o stdout de um servidor interno é uma substituição de comando, em vez de gravar a saída, o shell apenas anexa o texto de saída a ser enviado ao resultado da substituição do comando e não há necessidade de um fork.

Na verdade, fish (type echo) é mais parecido com ksh93 ${ type echo;} , pois nem cria um ambiente subshell. Com $(...) , ksh93 emula um ambiente subshell para que pareça que um processo filho foi bifurcado para interpretar o código nele e não faz isso para a variante ${ ...;}

ksh93$ a=1
ksh93$ echo "$(a=2; type echo) $a"
echo is a shell builtin 1
ksh93$ echo "${ a=3; type echo;} $a"
echo is a shell builtin 3

fish> set a 1
fish> echo (set a 2; type echo) $a
echo is a builtin 2

Você verá que em alguns shells que não fazem essa otimização, muitos dos builtins podem ser chamados de tal forma que, ao invés de escrever seus resultados, eles armazenam em uma variável.

Os mais óbvios são o padrão read e getopts que fazem isso por padrão (você faz IFS= read -r var em vez de var=$(line) ). bash e zsh também têm printf -v variable format args . zsh pode fazer o mesmo com seus stat , strftime ... builtins.

Algumas conchas também disponibilizam algumas informações automaticamente em algumas variáveis especiais, como ksh ' $SECONDS e $RANDOM encontradas em algumas outras conchas (e as comuns como $- , $# (equivalente a fish ' (count $argv) por exemplo)).

Em zsh , isso é generalizado para a maioria das informações internas do shell, portanto, você quase nunca precisa usar a substituição de comandos na saída de um arquivo interno. Por exemplo, ele tem matrizes associativas para a lista de builtins, palavras-chave, comandos ...

if (($+builtins[echo])); then
  echo echo is a builtin
fi
    
por 15.03.2018 / 23:00