在腳本執行時解析腳本是否普遍存在於 shell 中或存在於其他解釋器中,它是如何工作的?
我一直認為 shell 會解析整個腳本,構造一個 AST,然後從記憶體中執行那個 AST。但是,我剛剛閱讀了 Stéphane Chazelas 的評論,並測試了執行這個腳本,edit-while-executing.sh:
#!/bin/bash echo start sleep 10
然後在它睡覺的時候執行:
$ echo "echo end" >> edit-while-executing.sh
它使它在最後列印“結束”。
但是,當嘗試修改它時:
#!/bin/bash while true; do echo yes done
通過做:
$ printf "%s" "no " | dd of=edit-while-executing.sh conv=notrunc seek=35 bs=1
它沒有用,並繼續列印“是”。
我還想知道其他非shell解釋器是否也這樣工作,並嘗試了與python的第一個腳本等效的方法,但它不起作用。不過,也許 python 不再是解釋器,它更像是一個 JIT 編譯器。
所以重申我的問題,這是一種普遍存在於 shell 並且僅限於它們的行為,還是存在於其他解釋器(那些不被視為 shell 的解釋器)中?另外,這是如何工作的,我可以進行第一次修改但不能進行第二次修改?
所以,這會在 Bash/dash/ksh/zsh 中無限期地執行(或者至少直到你的磁碟填滿):
#!/bin/sh s=$0 foo() { echo "hello"; echo "foo" >> $s; sleep .1; } foo
需要注意的是,只有在 shell 讀取的最後一行之後添加到腳本文件中的內容才重要。如果輸入是管道,shell 不會返回重新讀取早期的部分,即使輸入是管道也無法做到這一點。
類似的結構在 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 時,列印
hello
,然後在一秒鐘後拋出語法錯誤。Python 看起來類似於帶有管道輸入的 Perl,即使解釋器在互動時執行 read-eval-print 循環。
除了逐行閱讀輸入腳本之外,至少 Bash 和 dash 一次將參數處理為
eval
一行:$ 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 立即給出錯誤。
與源腳本類似,這次 Zsh 也像 Bash 和 dash 一樣逐行執行:
$ cat sourceme.sh echo hello fi $ zsh -c '. ./sourceme.sh' hello ./sourceme.sh:2: parse error near `fi'
此功能存在於其他提供所謂的
read
eval
loop
. LISP 是一種非常古老的語言,具有這樣的功能,Common 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-output
和fresh-line
添加,其他 Common LISP 實現可能會也可能不會。)REPL 的其他內容包括 TCL(“被放射性 LISP 咬住的外殼”),其中包括帶有 Tk 的圖形內容
% 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