# 操作系统如何运行程序

人和操作系统如何交互？最早Ken Thompson设计Unix的时候，使用的是命令解释器，命令解释器接受用户的命令，然后解释他们并执行。

`Shell`是人和机器交互的界面，分为`GUI`和`命令行(CLI)`两种方式，这里重点介绍命令行的方式。概念`Shell`路易斯·普赞（Louis Pouzin）在1964年至1965年间首次提出，随后在Multics（MULTiplexed Information and Computing System）项目中首次被实现出来。肯·汤普逊（Ken Thompson）以Multics上的shell为范本实现的第一个Unix壳层（Unix shell）：`Thompson shell`。AT&T贝尔实验室的史蒂夫·伯恩在1977年实现了`Bourne shell`，或`sh`，它是Version 7 Unix默认的Unix shell，替代执行文件同为sh的Thompson shell。微软的Windows操作系统也提供了命令行壳层的功能，它们是Windows 95 / 98下的`command.com`、Windows NT内核下的`cmd.exe`以及`PowerShell`。

下面的`Python 列表`包含了Unix系统常见的`shell`:

```python
shells = [
    'Bourne shell（sh）',
    'Almquist shell（ash）',
    'Debian Almquist shell（dash）',
    'Bourne-Again shell（bash）',
    'Korn shell（ksh）',
    'Z shell（zsh）',
    'C shell（csh）',
    'TENEX C shell（tcsh）',
    'EMACS shell (eshell)',
    'Es shell（es）',
    'esh (Unix) – Easy Shell',
    'friendly interactive shell（fish）',
    'rc shell（rc）– shell for Plan 9 from Bell Labs and Unix',
    'scsh（Scheme Shell）',
    'Stand-alone Shell（sash）',
    'BeanShell（bsh，bsh.Interpreter , bsh.Console）',
    'Rhino JavaScript壳层（org.mozilla.javascript.tools.shell.Main）'
]
```

通过shell，我们可以让操作系统执行Python程序。而Python是解释型语言，源代码不是直接翻译成机器语言，而是先翻译成中间代码，再由解释器对中间代码进行解释运行。有两种执行Python程序的方式，下面的`Python 字典`包含了这两种方式的信息：

```python
# -*- coding: UTF-8 -*-
run = {
    "repl": {
        "title": "交互式编程( Interactive )",
        "desc": [
            "打开终端，输入 python 回车",
            "进入 Python 交互式命令行",
            "输入 print('monkey king is coding!')"
        ]
    },
    "source": {
        "title": "Python 源代源文件( File )",
        "desc": [
            "使用你喜欢的编辑器拷贝本练习的代码, 保存为run.py",
            "打开终端，cd 到 run.py 保存的目录",
            "输入 python run.py"
        ]
    }
}
```

![](https://userblink.csdnimg.cn/20211123/huanhuilong/pic/8555cd59ff00168ff58c23235a325867-0.png)

交互式命令行通常也称`REPL`，表示`Read-Eval-Print-Loop`，顾名思义是：`读取`-`评估`-`打印`-`循环`的意思。当你在`shell`环境里输入`python`并回车，就进入了`Python的REPL环境`，程序总是：

1. 等待用户输入 Python 代码
2. 当收到回车信号后就读取(`Read`)用户输入的 Python 代码
3. 解释读取到的 Python 代码(`Eval`)
4. 打印Python 代码的解释结果(`Print`)
5. 回到步骤1，循环(`Loop`)该过程

实际上，我们在之前的习题里已经见过这样的程序，经过重新认识后，现在可以再次编写一个这样的`REPL`程序，**功能需求如下**：

* 提示用户选择想要了解的Python 的两种运行方式
* 用户选择后输出对应Python 运行方式的基本说明，如果用户输入错误则提示后让用户重新选择
* 循环该过程直到用户输入`q`退出
* 最后输出用户学习了几种运行方式

基本代码框架如下：

```python
# -*- coding: UTF-8 -*-
if __name__ == '__main__':
    run = {
        "i": {
            "title": "交互式编程( Interactive )",
            "desc": [
                "打开终端，输入 python 回车",
                "进入 Python 交互式命令行",
                "输入 print('monkey king is coding!')"
            ]
        },
        "f": {
            "title": "Python 源代源文件( File )",
            "desc": [
                "使用你喜欢的编辑器拷贝本练习的代码, 保存为run.py",
                "打开终端，cd 到 run.py 保存的目录",
                "输入 python run.py"
            ]
        }
    }

    print("有两种基本的方式运行 Python")
    for s in run:
        item = run.get(s)
        print("* {}: {}".format(s, item['title']))

    has_learn_i = False
    has_learn_f = False

    # TODO(You): 请在此实现代码

    if has_learn_i and has_learn_f:
        print("[2/2]您已完成两种 Python 运行方式的学习")
    elif has_learn_f:
        print("[1/2]您已完成 Python 源代码方式运行学习")
    elif has_learn_i:
        print("[1/2]您已完成 Python 交互式命令行运行学习")
    else:
        print("[0/2]您似乎跳过了运行方式的学习？期待下次光临！")
```

程序的示例操作过程如下：

```bash
有两种基本的方式运行 Python
* repl: 交互式编程( Interactive )
* source: Python 源代源文件( File )
请选择你想了解的 Python 运行方式(输入：r/s选择，输入 q 退出)：r
0. 打开终端，输入 python 回车
1. 进入 Python 交互式命令行
2. 输入 print('monkey king is coding!')
请选择你想了解的 Python 运行方式(输入：r/s选择，输入 q 退出)：s
0. 使用你喜欢的编辑器拷贝本练习的代码, 保存为run.py
1. 打开终端，cd 到 run.py 保存的目录
2. 输入 python run.py
请选择你想了解的 Python 运行方式(输入：r/s选择，输入 q 退出)：q
[2/2]您已完成两种 Python 运行方式的学习
```

以下选项是对代码中`TODO`部分的多种实现，你能找出实现<span style="color:red">错误</span>的选项吗？

## template

```python
if __name__ == '__main__':
    run = {
        "repl": {
            "title": "交互式编程( Interactive )",
            "desc": [
                "打开终端，输入 python 回车",
                "进入 Python 交互式命令行",
                "输入 print('monkey king is coding!')"
            ]
        },
        "source": {
            "title": "Python 源代源文件( File )",
            "desc": [
                "使用你喜欢的编辑器拷贝本练习的代码, 保存为run.py",
                "打开终端，cd 到 run.py 保存的目录",
                "输入 python run.py"
            ]
        }
    }

    print("有两种基本的方式运行 Python")
    shoutcut_keys = {}
    for name in run:
        key = name[0].lower()
        shoutcut_keys[key] = name
        item = run.get(name)
        print("* {}: {}".format(name, item['title']))

    has_learn_repl = False
    has_learn_source = False

    while True:
        ret = input("请选择你想了解的 Python 运行方式(输入：r/s选择，输入 q 退出)：")
        if ret == 'q':
            break
        elif ret == 'r':
            has_learn_repl = True
        elif ret == 's':
            has_learn_source = True

        name = shoutcut_keys.get(ret)
        if name is None:
            print("[错误] 不支持的运行方式")
        else:
            item = run.get(name)
            desc = item['desc']
            for i in range(0, len(desc)):
                print("{}. {}".format(i, desc[i]))

    if has_learn_repl and has_learn_source:
        print("[2/2]您已完成两种 Python 运行方式的学习")
    elif has_learn_source:
        print("[1/2]您已完成 Python 源代码方式运行学习")
    elif has_learn_repl:
        print("[1/2]您已完成 Python 交互式命令行运行学习")
    else:
        print("[0/2]您似乎跳过了运行方式的学习？期待下次光临！")
```

## 答案

```python
while True:
    ret = input("请选择你想了解的 Python 运行方式(输入：r/s选择，输入 q 退出)：")
    if ret == 'r' or ret == 's':
        has_learn_repl = ret=='r'
        has_learn_source = ret=='s'
        desc = run.get(shoutcut_keys.get(ret))['desc']
        for i in range(0, len(desc)):
            print("{}. {}".format(i, desc[i]))
    elif ret != 'q':
        print("[错误] 不支持的运行方式")
        break
    else:
        pass
```

## 选项

### 普通实现

```python
while True:
    ret = input("请选择你想了解的 Python 运行方式(输入：r/s选择，输入 q 退出)：")
    if ret == 'q':
        break
    elif ret == 'r':
        has_learn_repl = True
    elif ret == 's':
        has_learn_source = True

    name = shoutcut_keys.get(ret)
    if name is None:
        print("[错误] 不支持的运行方式")
    else:
        item = run.get(name)
        desc = item['desc']
        for i in range(0, len(desc)):
            print("{}. {}".format(i, desc[i]))
```

### 合并错误处理

```python
while True:
    ret = input("请选择你想了解的 Python 运行方式(输入：r/s选择，输入 q 退出)：")

    name = shoutcut_keys.get(ret)
    if name is None:
        if ret=='q':
            break
        else:
            print("[错误] 不支持的运行方式")
            continue
    
    has_learn_repl = ret=='r'
    has_learn_source = ret=='s'

    desc = run.get(name)['desc']
    for i in range(0, len(desc)):
        print("{}. {}".format(i, desc[i]))
```

### 极简模式

```python
while True:
    ret = input("请选择你想了解的 Python 运行方式(输入：r/s选择，输入 q 退出)：")
    if ret == 'r' or ret == 's':
        if ret=='r':
            has_learn_repl = True
        else:
            has_learn_source = True
        item = run.get(shoutcut_keys.get(ret))
        desc = item['desc']
        for i in range(0, len(desc)):
            print("{}. {}".format(i, desc[i]))
    elif ret == 'q':
        break
    else:
        print("[错误] 不支持的运行方式")
```
