이전 글에서는 쉘 메인 루프를 생성하고 명령 입력을 분할한 뒤 fork
, exec
을 통해 명령을 실행해 보았습니다. 이 부분에서는 나머지 문제를 해결하겠습니다. 우선, cd test_dir2
명령은 현재 디렉토리를 수정할 수 없습니다. 둘째, 여전히 셸에서 정상적으로 종료할 수 없습니다.
"cd test_dir2
은 현재 디렉터리를 수정할 수 없습니다." 이 문장은 맞지만 어떤 의미에서는 틀립니다. 명령을 실행한 후에도 여전히 같은 디렉터리에 있다는 점에서는 맞습니다. 그러나 실제로는 디렉터리가 수정되었는데, 자식 프로세스에서 수정된 것입니다.
하위 프로세스를 포크한 후 명령을 실행한 프로세스가 상위 프로세스에서 발생하지 않았다는 점을 기억하세요. 결과적으로 부모 프로세스의 디렉터리가 아닌 자식 프로세스의 현재 디렉터리만 변경됩니다.
그런 다음 하위 프로세스가 종료되고 상위 프로세스는 그대로 디렉터리에서 계속 실행됩니다.
그러므로 쉘 자체에 관련된 명령은 내장 명령이어야 합니다. 포크가 아닌 쉘 프로세스에서 실행되어야 합니다.
cd
명령부터 시작해 보겠습니다.
먼저 builtins
디렉터리를 만듭니다. 모든 내장 명령은 이 디렉토리에 배치됩니다.
yosh_project |-- yosh |-- builtins | |-- __init__.py | |-- cd.py |-- __init__.py |-- shell.py
cd.py
에서는 os.chdir
시스템 호출을 사용하여 자체 cd
명령을 구현합니다.
import os from yosh.constants import * def cd(args): os.chdir(args[0]) return SHELL_STATUS_RUN
내장 함수에서 셸의 실행 상태를 반환한다는 점에 유의하세요. 그래서 우리 프로젝트에서 상수를 계속 사용할 수 있도록 yosh/constants.py
로 옮겼습니다.
yosh_project |-- yosh |-- builtins | |-- __init__.py | |-- cd.py |-- __init__.py |-- constants.py |-- shell.py
constants.py
에서는 모든 상태 상수를 여기에 넣습니다.
이제 내장 cd
이 준비되었습니다. 이러한 내장 기능을 처리하도록 shell.py
을 수정해 보겠습니다.
SHELL_STATUS_STOP = 0 SHELL_STATUS_RUN = 1
우리는 Python 사전 변수built_in_cmds
를 해시 맵으로 사용하여 내장 함수를 저장합니다. execute
함수에서 명령의 이름과 매개변수를 추출합니다. 명령이 해시 맵에 있으면 해당 내장 함수가 호출됩니다.
(팁: built_in_cmds[cmd_name]
은 인수를 사용하여 직접 호출할 수 있는 함수 참조를 반환합니다.)
내장된 cd
함수를 사용할 준비가 거의 완료되었습니다. 마지막 단계는 cd
매핑에 built_in_cmds
함수를 추가하는 것입니다.
... ### 导入常量 from yosh.constants import * ### 使用哈希映射来存储内建的函数名及其引用 built_in_cmds = {} def tokenize(string): return shlex.split(string) def execute(cmd_tokens): ### 从元组中分拆命令名称与参数 cmd_name = cmd_tokens[0] cmd_args = cmd_tokens[1:] ### 如果该命令是一个内建命令,使用参数调用该函数 if cmd_name in built_in_cmds: return built_in_cmds[cmd_name](cmd_args) ...
내장 명령 해시 맵에 내장 함수를 추가하기 위해 register_command
함수를 정의했습니다. 다음으로 init
함수를 정의하고 여기에 내장 cd
함수를 등록합니다.
이 줄을 주목하세요 register_command("cd", cd)
. 첫 번째 매개변수는 명령의 이름입니다. 두 번째 매개변수는 함수 참조입니다. 두 번째 매개변수 cd
가 yosh/builtins/cd.py
의 cd
함수 참조를 참조할 수 있도록 하려면 yosh/builtins/__init__.py
파일에 다음 코드 줄을 배치해야 합니다.
... ### 导入所有内建函数引用 from yosh.builtins import * ... ### 注册内建函数到内建命令的哈希映射中 def register_command(name, func): built_in_cmds[name] = func ### 在此注册所有的内建命令 def init(): register_command("cd", cd) def main(): ###在开始主循环之前初始化 shell init() shell_loop()
그래서 yosh/shell.py
에서 yosh.builtins
에서 *
을 가져올 때 yosh.builtins
을 통해 가져온 cd
함수 참조를 얻을 수 있습니다.
코드가 준비되었습니다. 쉘 yosh
을 python -m yosh.shell
과 동일한 디렉토리에 있는 모듈로 실행해 보겠습니다.
이제 cd
명령이 쉘 디렉토리를 올바르게 수정하는 동시에 내장되지 않은 명령도 계속 작동합니다. 매우 좋은!
드디어 마지막 작품이 나왔습니다. 우아하게 퇴장하세요.
쉘 상태를 SHELL_STATUS_STOP
으로 수정할 수 있는 함수가 필요합니다. 이런 식으로 쉘 루프가 자연스럽게 끝날 수 있으며 쉘은 끝에 도달하여 종료됩니다.
은 cd
과 동일합니다. 자식 프로세스에서 exit
함수를 분기하여 실행하면 부모 프로세스에는 아무런 영향이 없습니다. 따라서 exit
함수는 쉘에 내장되어 있어야 합니다.
여기서 시작해 보겠습니다. builtins
디렉터리에 exit.py
이라는 새 파일을 만듭니다.
from yosh.builtins.cd import *
exit.py
는 메인 루프를 종료할 수 있는 상태만 반환하는 exit
함수를 정의합니다.
yosh_project |-- yosh |-- builtins | |-- __init__.py | |-- cd.py | |-- exit.py |-- __init__.py |-- constants.py |-- shell.py
그런 다음 yosh/builtins/__init__.py
파일에 있는 exit
함수 참조를 가져옵니다.
from yosh.constants import * def exit(args): return SHELL_STATUS_STOP
마지막으로 shell.py
의 init()
함수에 exit
명령을 등록합니다.
바로 그거야!
실행해 보세요python -m yosh.shell
. 이제 exit
를 입력하여 프로그램을 정상적으로 종료할 수 있습니다.
저yosh
(y저희 >ell) 과정만큼 재미있게 만드셨기를 바랍니다. 하지만 내 버전은 아직 초기 단계다. 나는 쉘을 충돌시킬 수 있는 일부 특수 사례를 다루지 않았습니다. 내가 다루지 않은 내장 명령이 많이 있습니다. 성능을 향상시키기 위해 일부 비내장 명령을 내장 명령으로 구현할 수도 있습니다(새 프로세스 생성 시간을 피하기 위해). 동시에 많은 기능이 아직 구현되지 않았습니다. https://github.com/supasate/yosh에 소스코드를 제공했습니다. 마음껏 포크해서 사용해 보세요. yosh
이제 진정한 나만의 쉘을 만들 차례입니다.
즐거운 코딩하세요!
위 내용은 Python으로 자신만의 셸을 만드는 방법(2부)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!