スクリプトの実行中に解析スクリプトはシェルに共通に適用されますか、それとも別のインタプリタに存在しますか?どのように動作しますか?

スクリプトの実行中に解析スクリプトはシェルに共通に適用されますか、それとも別のインタプリタに存在しますか?どのように動作しますか?

私はいつもシェルがスクリプト全体を解析してASTを構築し、メモリからASTを実行すると思いました。ところで先ほど読んだ。コメント: Stéphane Chazelasそして、edit-while-executing.shスクリプトの実行をテストします。

#!/bin/bash

echo start
sleep 10

その後、睡眠中にこれを実行します。

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

それがすることは最後に「end」を印刷することです。

ただし、これを修正しようとすると、次のようになります。

#!/bin/bash

while true; do
  echo yes
done

以下を実行してください。

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

動作せず、引き続き「はい」と印刷されます。

また、他のビシェルインタプリタがこのように動作するかどうか疑問に思い、Pythonの最初のスクリプトと同等のものを試しましたが、うまくいきませんでした.しかし、おそらくPythonはもはやインタプリタではなく、JITコンパイラに近いでしょう。

だから私の質問をもう一度言うと、この動作はシェルで一般的であり、シェルに限定されていますか、それとも他のインタプリタ(シェルとは見なされないもの)にも存在しますか?また、最初の修正はできますが、2番目の修正はできないようにするにはどうすればよいですか?

答え1

LISPは、これらのread eval print loop機能を備えた非常に古い言語です。一般的なLISPには、read式を読み取って評価の(+ 2 2)ために渡す機能がありますeval(実際のコードでは、さまざまな理由からセキュリティ上の理由から望ましくない可能性があります)。これを行うために):

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

SEXP
* (print (eval sexp))

4
4

あまりにも多くの機能やデバッグ、または他の何もせずに非常に単純なREPLを定義することもできますが、ここにはREPL部分が表示されます。

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

YAREPL
* (yarepl)
(* 4 2)

8
(print "hi")

"hi"
"hi"

基本的には、銘板に書かれているようにデータを読み、評価し、印刷してから(衝突が発生せずに電源やデバイスに電力を供給する他のものがあると仮定)、再読み込みに戻るため、ASTを事前に構築する必要はありません。 (表示上の理由で SBCL によって要求され、追加されておりforce-outputfresh-line他の共通 LISP 実装にはあってもなくてもよい。)

REPLの他のコンテンツには、Tkのグラフィックコンテンツを含むTCL(「The Shell Bitten by Radioactive LISP」)が含まれます。

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

または、ここで温度変換を実行する関数を定義しますf>c(「ok」が追加されます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

答え2

したがって、これはBash / dash / ksh / zshで(または少なくともディスクがいっぱいになるまで)無期限に実行されます。

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

シェルから読み取った最後の行以降にスクリプトファイルに追加された内容だけが意味があることに注意してくださいシェルは戻って前の部分を読み直さず、入力がパイプであってもそうすることはできません。

ファイルを実行する前にファイル全体を読み取るPerlでは、同様の設定は機能しません。

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

パイプを介して入力が提供されても同じことが起こるのを見ることができます。 1秒後に構文エラーが発生します(この場合のみ)。

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

そして、同じスクリプトがBashなどにパイプされて印刷され、hello1秒後に構文エラーが発生します。

Pythonはパイプ入力を持つPerlと似ていますが、インタプリタは対話的に読み取り - 評価 - 印刷ループを実行します。


入力スクリプトを1行ずつ読み込むことに加えて、Bashとダッシュは引数evalを一度に1行以上処理します。

$ 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とkshはすぐにエラーを発生させます。

同様に、ソーススクリプトの場合、今回はBashやダッシュと同様にZshも1行ずつ実行されます。

$ cat sourceme.sh
echo hello
fi
$ zsh -c '. ./sourceme.sh'
hello
./sourceme.sh:2: parse error near `fi'

答え3

少なくとも一種の甲殻類である魚は、これらの行動を示していません(しかし、魚は他の点で珍しいです)。

% 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

(この回答の以前のバージョンでは、PythonとRubyについて誤った観察を行いました。)

関連情報