Está analisando scripts no script-runtime onipresente para shells ou presentes em outros interpretadores e como isso funciona?

7

Sempre achei que shells analisam scripts inteiros, construindo uma AST e, em seguida, executam essa AST da memória. No entanto, acabei de ler um comentário de Stéphane Chazelas e testei a execução desse script , edit-while-executing.sh:

#!/bin/bash

echo start
sleep 10

e, em seguida, enquanto estava dormindo:

$ echo "echo end" >> edit-while-executing.sh

e funcionou para fazer com que ele imprimisse "fim" no final.

No entanto, ao tentar modificar isso:

#!/bin/bash

while true; do
  echo yes
done

fazendo:

$ printf "%s" "no " | dd of=edit-while-executing.sh conv=notrunc seek=35 bs=1

Não funcionou e continuou imprimindo "sim".

Eu também queria saber se outros interpretadores não-shell também funcionavam assim, e tentaram o equivalente ao primeiro script com python, mas não funcionou. Porém, talvez o python não seja mais um interpretador e seja mais um compilador JIT.

Então, para reiterar minha pergunta, este é um comportamento onipresente para as conchas e limitado a elas ou também presente em outros intérpretes (aqueles não considerados como conchas)? Também como isso funciona de tal forma que eu poderia fazer a primeira modificação, mas não o segundo?

    
por JoL 13.06.2018 / 18:01

3 respostas

2

Portanto, isso é executado indefinidamente em Bash / dash / ksh / zsh (ou pelo menos até o seu disco ficar cheio):

#!/bin/sh
s=$0
foo() { echo "hello"; echo "foo" >> $s; sleep .1; }
foo

A única coisa a notar é que apenas o material acrescentado foi adicionado ao arquivo de script após a última linha que o shell leu. As conchas não voltam a reler as partes anteriores, o que elas nem poderiam fazer, se a entrada fosse um cano.

A construção similar não funciona em Perl, ele lê todo o arquivo antes de ser executado.

#!/usr/bin/perl -l    
open $fd, ">>", $0;
sub foo { print "hello"; print $fd 'foo;' }
foo;

Podemos ver que isso também acontece quando é dada entrada através de um pipe. Isto dá um erro de sintaxe (e somente isso) após 1 segundo:

$ (echo 'printf "hello\n";' ; sleep 1 ; echo 'if' ) | perl 

Embora o mesmo script seja canalizado para, por ex. Bash, imprime hello e, em seguida, lança o erro de sintaxe um segundo depois.

O Python é parecido com o Perl com entrada canalizada, mesmo que o interpretador execute um loop de leitura e impressão quando estiver interativo.

Além de ler o script de entrada linha por linha, defina pelo menos argumentos de processo Bash e traço como eval uma linha por vez:

$ cat evaltest.sh
var='echo hello
fi'
eval "$var"
$ bash evaltest.sh
hello
evaltest.sh: eval: line 4: syntax error near unexpected token 'fi'
evaltest.sh: eval: line 4: 'fi'

Zsh e ksh informam o erro imediatamente.

Similarmente para scripts originados, desta vez o Zsh também roda linha por linha, assim como o Bash e o dash:

$ cat sourceme.sh
echo hello
fi
$ zsh -c '. ./sourceme.sh'
hello
./sourceme.sh:2: parse error near 'fi'
    
por 13.06.2018 / 19:39
3

Este recurso está presente em outros intérpretes que oferecem o que é chamado read eval print loop . LISP é uma linguagem bastante antiga com tal recurso, e Common LISP tem uma função read que lerá aqui a expressão (+ 2 2) que pode então ser passada para eval para avaliação (embora em código real você não queira fazer desta forma por várias razões de segurança):

% sbcl
* (defparameter sexp (read))
(+ 2 2)

SEXP
* (print (eval sexp))

4
4

também podemos definir nosso próprio REPL muito simples, sem muito em termos de recursos ou depuração ou praticamente qualquer outra coisa, mas isso mostra as partes do REPL:

* (defun yarepl () (loop (print (eval (read))) (force-output) (fresh-line)))

YAREPL
* (yarepl)
(* 4 2)

8
(print "hi")

"hi"
"hi"

Basicamente, como se diz na placa de identificação, os dados são lidos, avaliados, impressos e depois (assumindo que não há nada quebrado e ainda há eletricidade ou algo ligando o dispositivo) retorna à leitura Não é necessário construir um AST em avançar. (A SBCL precisa das adições force-output e fresh-line por motivos de exibição, outras implementações comuns do LISP podem ou não.)

Outras coisas com o REPL incluem TCL ("um shell mordido por um LISP radioativo") que inclui elementos gráficos com Tk

% wish
wish> set msg "hello"
hello
wish> pack [label .msg -textvariable msg]
wish> wm geometry . 500x500
wish> exit

Ou FORTH aqui para definir uma função f>c para fazer a conversão de temperatura (o "ok" é adicionado por gforth ):

% gforth
Gforth 0.7.3, Copyright (C) 1995-2008 Free Software Foundation, Inc.
Gforth comes with ABSOLUTELY NO WARRANTY; for details type 'license'
Type 'bye' to exit
: f>c ( f -- c ) 32 - 5 9 */ cr . cr ;  ok
-40 f>c
-40
 ok
100 f>c
37
 ok
bye
    
por 13.06.2018 / 19:10
1

Pelo menos uma casca, peixe, não exibe esse comportamento (mas o peixe é incomum de outras maneiras):

% for sh in zsh mksh fish dash bash tcsh; do echo 'sleep 5' > foo.$sh; $sh foo.$sh & sleep 1; echo 'echo $0' >> foo.$sh; fg; done
[2] 7381
[2]  - 7381 running    $sh foo.$sh
foo.zsh
[2] 7385
[2]  - 7385 running    $sh foo.$sh
foo.mksh
[2] 7387
[2]  - 7387 running    $sh foo.$sh
[2] 7390
[2]  - 7390 running    $sh foo.$sh
foo.dash
[2] 7393
[2]  - 7393 running    $sh foo.$sh
foo.bash
[2] 7415
[2]  - 7415 running    $sh foo.$sh
foo.tcsh

(Uma versão anterior desta resposta tinha observações erradas de Python e Ruby.)

    
por 13.06.2018 / 18:14