나는 항상 쉘이 전체 스크립트를 구문 분석하고 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 컴파일러에 더 가깝습니다.
그래서 내 질문을 다시 말하면, 이 동작은 쉘에서 일반적이고 쉘에만 국한됩니까, 아니면 다른 인터프리터(쉘로 간주되지 않는 것)에도 존재합니까? 또한 첫 번째 수정은 할 수 있지만 두 번째 수정은 할 수 없도록 하려면 어떻게 해야 합니까?
답변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-output
되고 추가되었으며 fresh-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 등으로 파이프되어 인쇄된 다음 hello
1초 후에 구문 오류가 발생합니다.
Python은 파이프 입력이 있는 Perl과 유사해 보이지만 인터프리터는 대화식으로 읽기-평가-인쇄 루프를 실행합니다.
입력 스크립트를 한 줄씩 읽는 것 외에도 Bash와 대시는 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는 즉시 오류를 발생시킵니다.
마찬가지로 소스 스크립트의 경우 이번에는 Bash 및 대시와 마찬가지로 Zsh도 한 줄씩 실행됩니다.
$ 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에 대해 잘못된 관찰을 했습니다.)