mirror of
https://github.com/LCTT/TranslateProject.git
synced 2025-01-22 23:00:57 +08:00
commit
69c331feb9
@ -1,18 +1,18 @@
|
||||
从 ISO 和在线仓库创建一个 YUM 仓库
|
||||
从 ISO 和在线仓库创建一个 Yum 仓库
|
||||
======
|
||||
|
||||
YUM 是 Centos/RHEL/Fedora 中最重要的工具之一。尽管在 Fedora 的最新版本中,它已经被 DNF 所取代,但这并不意味着它已经成功了。它仍然被广泛用于安装 rpm 包,我们已经在前面的教程([**在这里阅读**] [1])中用示例讨论了 YUM。
|
||||
Yum 是 Centos/RHEL/Fedora 中最重要的工具之一。尽管在 Fedora 的最新版本中,它已经被 DNF 所取代,但这并不意味着它自生自灭了。它仍然被广泛用于安装 rpm 包,我们已经在前面的教程([**在这里阅读**] [1])中用示例讨论了 Yum。
|
||||
|
||||
在本教程中,我们将学习创建一个本地 YUM 仓库,首先使用系统的 ISO 镜像,然后创建一个在线 yum 仓库的镜像。
|
||||
在本教程中,我们将学习创建一个本地 Yum 仓库,首先使用系统的 ISO 镜像,然后创建一个在线 Yum 仓库的镜像。
|
||||
|
||||
### 用 DVD ISO 创建 YUM
|
||||
### 用 DVD ISO 创建 Yum
|
||||
|
||||
我们在本教程中使用 Centos 7 dvd,同样的过程也应该可以用在 RHEL 7 上。
|
||||
|
||||
首先在根文件夹中创建一个名为 YUM 的目录
|
||||
首先在根文件夹中创建一个名为 Yum 的目录
|
||||
|
||||
```
|
||||
$ mkdir /YUM-
|
||||
$ mkdir /YUM
|
||||
```
|
||||
|
||||
然后挂载 Centos 7 ISO:
|
||||
@ -21,7 +21,7 @@ $ mkdir /YUM-
|
||||
$ mount -t iso9660 -o loop /home/dan/Centos-7-x86_x64-DVD.iso /mnt/iso/
|
||||
```
|
||||
|
||||
接下来,从挂载的 ISO 中复制软件包到 /YUM 中。当所有的软件包都被复制到系统中后,我们将安装创建 YUM 所需的软件包。打开 /YUM 并安装以下 RPM 包:
|
||||
接下来,从挂载的 ISO 中复制软件包到 `/YUM` 中。当所有的软件包都被复制到系统中后,我们将安装创建 Yum 所需的软件包。打开 `/YUM` 并安装以下 RPM 包:
|
||||
|
||||
```
|
||||
$ rpm -ivh deltarpm
|
||||
@ -29,7 +29,7 @@ $ rpm -ivh python-deltarpm
|
||||
$ rpm -ivh createrepo
|
||||
```
|
||||
|
||||
安装完成后,我们将在 **/etc/yum.repos.d** 中创建一个名 为 **“local.repo”** 的文件,其中包含所有的 yum 信息。
|
||||
安装完成后,我们将在 `/etc/yum.repos.d` 中创建一个名 为 `local.repo` 的文件,其中包含所有的 Yum 信息。
|
||||
|
||||
```
|
||||
$ vi /etc/yum.repos.d/local.repo
|
||||
@ -49,28 +49,28 @@ enabled=1
|
||||
$ createrepo -v /YUM
|
||||
```
|
||||
|
||||
创建仓库数据需要一些时间。一切完成后,请运行
|
||||
创建仓库数据需要一些时间。一切完成后,请运行:
|
||||
|
||||
```
|
||||
$ yum clean all
|
||||
```
|
||||
|
||||
清理缓存,然后运行
|
||||
清理缓存,然后运行:
|
||||
|
||||
```
|
||||
$ yum repolist
|
||||
```
|
||||
|
||||
检查所有仓库列表。你应该在列表中看到 “local.repo”。
|
||||
检查所有仓库列表。你应该在列表中看到 `local.repo`。
|
||||
|
||||
|
||||
### 使用在线仓库创建镜像 YUM 仓库
|
||||
### 使用在线仓库创建镜像 Yum 仓库
|
||||
|
||||
创建在线 yum 的过程与使用 ISO 镜像创建 yum 类似,只是我们将从在线仓库而不是 ISO 中获取 rpm 软件包。
|
||||
创建在线 Yum 的过程与使用 ISO 镜像创建 Yum 类似,只是我们将从在线仓库而不是 ISO 中获取 rpm 软件包。
|
||||
|
||||
首先,我们需要找到一个在线仓库来获取最新的软件包。建议你找一个离你位置最近的在线 yum 仓库,以优化下载速度。我们将使用下面的镜像,你可以从[ CENTOS 镜像列表][2]中选择一个离你最近的镜像。
|
||||
首先,我们需要找到一个在线仓库来获取最新的软件包。建议你找一个离你位置最近的在线 Yum 仓库,以优化下载速度。我们将使用下面的镜像,你可以从 [CENTOS 镜像列表][2]中选择一个离你最近的镜像。
|
||||
|
||||
选择镜像之后,我们将使用 rsync 将该镜像与我们的系统同步,但在此之前,请确保你服务器上有足够的空间。
|
||||
选择镜像之后,我们将使用 `rsync` 将该镜像与我们的系统同步,但在此之前,请确保你服务器上有足够的空间。
|
||||
|
||||
```
|
||||
$ rsync -avz rsync://mirror.fibergrid.in/centos/7.2/os/x86_64/Packages/s/ /YUM
|
||||
@ -96,9 +96,9 @@ $ crontab -e
|
||||
30 12 * * * rsync -avz http://mirror.centos.org/centos/7/os/x86_64/Packages/ /YUM
|
||||
```
|
||||
|
||||
这会在每晚 12:30 同步 yum。还请记住在 /etc/yum.repos.d 中创建仓库配置文件,就像我们上面所做的一样。
|
||||
这会在每晚 12:30 同步 Yum。还请记住在 `/etc/yum.repos.d` 中创建仓库配置文件,就像我们上面所做的一样。
|
||||
|
||||
就是这样,你现在有你自己的 yum 仓库来使用。如果你喜欢它,请分享这篇文章,并在下面的评论栏留下你的意见/疑问。
|
||||
就是这样,你现在使用你自己的 Yum 仓库了。如果你喜欢它,请分享这篇文章,并在下面的评论栏留下你的意见/疑问。
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@ -107,7 +107,7 @@ via: http://linuxtechlab.com/creating-yum-repository-iso-online-repo/
|
||||
|
||||
作者:[Shusain][a]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -7,52 +7,49 @@
|
||||
|
||||
* 快速搜索
|
||||
* 更复杂的搜索条件
|
||||
* 连接条件
|
||||
* 组合条件
|
||||
* 反转条件
|
||||
* 简单和详细的回应
|
||||
* 寻找重复的文件
|
||||
|
||||
有很多有用的命令可以搜索文件,**find** 命令可能是其中最有名的,但它不是唯一的命令,也不一定总是找到目标文件的最快方法。
|
||||
有很多有用的命令可以搜索文件,`find` 命令可能是其中最有名的,但它不是唯一的命令,也不一定总是找到目标文件的最快方法。
|
||||
|
||||
### 快速搜索命令:which 和 locate
|
||||
|
||||
搜索文件的最简单的命令可能就是 **which** 和 **locate** 了,但二者都有一些局限性。**which** 命令只会在系统定义的搜索路径中,查找可执行的文件,通常用于识别命令。如果您对输入 which 时会运行的命令感到好奇,您可以使用命令 which which,它会指向对应的可执行文件。
|
||||
搜索文件的最简单的命令可能就是 `which` 和 `locate` 了,但二者都有一些局限性。`which` 命令只会在系统定义的搜索路径中,查找可执行的文件,通常用于识别命令。如果您对输入 `which` 时会运行哪个命令感到好奇,您可以使用命令 `which which`,它会指出对应的可执行文件。
|
||||
|
||||
```
|
||||
$ which which
|
||||
/usr/bin/which
|
||||
|
||||
```
|
||||
|
||||
**which** 命令会显示它找到的第一个以相应名称命名的可执行文件(也就是使用该命令时将运行的那个文件),然后停止。
|
||||
`which` 命令会显示它找到的第一个以相应名称命名的可执行文件(也就是使用该命令时将运行的那个文件),然后停止。
|
||||
|
||||
**locate** 命令更大方一点,它可以查找任意数量的文件,但它也有一个限制:仅当文件名被包含在由 **updatedb** 命令准备的数据库时才有效。该文件可能会存储在某个位置,如 /var/lib/mlocate/mlocate.db,但不能用 locate 以外的任何命令读取。这个文件的更新通常是通过每天通过 cron 运行的 updatedb 进行的。
|
||||
`locate` 命令更大方一点,它可以查找任意数量的文件,但它也有一个限制:仅当文件名被包含在由 `updatedb` 命令构建的数据库时才有效。该文件可能会存储在某个位置,如 `/var/lib/mlocate/mlocate.db`,但不能用 `locate` 以外的任何命令读取。这个文件的更新通常是通过每天通过 cron 运行的 `updatedb` 进行的。
|
||||
|
||||
简单的 **find** 命令不需要太多限制,不过它需要搜索的起点和指定搜索条件。最简单的 find 命令:按文件名搜索文件。如下所示:
|
||||
简单的 `find` 命令没有太多限制,不过它需要指定搜索的起点和搜索条件。最简单的 `find` 命令:按文件名搜索文件。如下所示:
|
||||
|
||||
```
|
||||
$ find . -name runme
|
||||
./bin/runme
|
||||
|
||||
```
|
||||
|
||||
如上所示,通过文件名搜索文件系统的当前位置将会搜索所有子目录,除非您指定了搜索深度。
|
||||
|
||||
### 不仅仅是文件名
|
||||
|
||||
**find** 命令允许您搜索除文件名以外的多种条件,包括文件所有者、组、权限、大小、修改时间、缺少所有者或组和文件类型等。除了查找文件外,您还可以删除文件、对其进行重命名、更改所有者、更改权限和对文件运行几乎任何命令。
|
||||
`find` 命令允许您搜索除文件名以外的多种条件,包括文件所有者、组、权限、大小、修改时间、缺少所有者或组,和文件类型等。除了查找文件外,您还可以删除文件、对其进行重命名、更改所有者、更改权限和对找到的文件运行几乎任何命令。
|
||||
|
||||
下面两条命令会查找:在当前目录中 root 用户拥有的文件,以及非指定用户(在本例中为 shs)拥有的文件。在这个例子中,两个输出是一样的,但并不总是如此。
|
||||
下面两条命令会查找:在当前目录中 root 用户拥有的文件,以及不被指定用户(在本例中为 shs)所拥有的文件。在这个例子中,两个输出是一样的,但并不总是如此。
|
||||
|
||||
```
|
||||
$ find . -user root -ls
|
||||
396926 0 lrwxrwxrwx 1 root root 21 Sep 21 09:03 ./xyz -> /home/peanut/xyz
|
||||
$ find . ! -user shs -ls
|
||||
396926 0 lrwxrwxrwx 1 root root 21 Sep 21 09:03 ./xyz -> /home/peanut/xyz
|
||||
|
||||
```
|
||||
|
||||
感叹号“!”字符代表“非”:反转跟随其后的条件。
|
||||
感叹号 `!` 字符代表“非”:反转跟随其后的条件。
|
||||
|
||||
下面的命令将查找具有特定权限的文件:
|
||||
|
||||
@ -61,7 +58,6 @@ $ find . -perm 750 -ls
|
||||
397176 4 -rwxr-x--- 1 shs shs 115 Sep 14 13:52 ./ll
|
||||
398209 4 -rwxr-x--- 1 shs shs 117 Sep 21 08:55 ./get-updates
|
||||
397145 4 drwxr-x--- 2 shs shs 4096 Sep 14 15:42 ./newdir
|
||||
|
||||
```
|
||||
|
||||
接下来的命令显示具有 777 权限的非符号链接文件:
|
||||
@ -70,19 +66,17 @@ $ find . -perm 750 -ls
|
||||
$ sudo find /home -perm 777 ! -type l -ls
|
||||
397132 4 -rwxrwxrwx 1 shs shs 18 Sep 15 16:06 /home/shs/bin/runme
|
||||
396949 4 -rwxrwxrwx 1 root root 558 Sep 21 11:21 /home/oops
|
||||
|
||||
```
|
||||
|
||||
以下命令将查找大小超过千兆字节的文件。请注意,我们找到了一个非常有趣的文件。它在 ELF 核心文件格式中代表该系统的物理内存。
|
||||
以下命令将查找大小超过千兆字节的文件。请注意,我们找到了一个非常有趣的文件。它以 ELF core 文件格式表示了该系统的物理内存。
|
||||
|
||||
```
|
||||
$ sudo find / -size +1G -ls
|
||||
4026531994 0 -r-------- 1 root root 140737477881856 Sep 21 11:23 /proc/kcore
|
||||
1444722 15332 -rw-rw-r-- 1 shs shs 1609039872 Sep 13 15:55 /home/shs/Downloads/ubuntu-17.04-desktop-amd64.iso
|
||||
|
||||
```
|
||||
|
||||
只要您知道 find 命令是如何描述文件类型的,就可以通过文件类型来查找文件。
|
||||
只要您知道 `find` 命令是如何描述文件类型的,就可以通过文件类型来查找文件。
|
||||
|
||||
```
|
||||
b = 块设备文件
|
||||
@ -93,7 +87,6 @@ f = 常规文件
|
||||
l = 符号链接
|
||||
s = 套接字
|
||||
D = 门(仅限 Solaris)
|
||||
|
||||
```
|
||||
|
||||
在下面的命令中,我们要寻找符号链接和套接字:
|
||||
@ -103,28 +96,25 @@ $ find . -type l -ls
|
||||
396926 0 lrwxrwxrwx 1 root root 21 Sep 21 09:03 ./whatever -> /home/peanut/whatever
|
||||
$ find . -type s -ls
|
||||
395256 0 srwxrwxr-x 1 shs shs 0 Sep 21 08:50 ./.gnupg/S.gpg-agent
|
||||
|
||||
```
|
||||
|
||||
您还可以根据 inode 数字来搜索文件:
|
||||
您还可以根据 inode 号来搜索文件:
|
||||
|
||||
```
|
||||
$ find . -inum 397132 -ls
|
||||
397132 4 -rwx------ 1 shs shs 18 Sep 15 16:06 ./bin/runme
|
||||
|
||||
```
|
||||
|
||||
另一种通过 inode 搜索文件的方法是使用 **debugfs** 命令。在大的文件系统上,这个命令可能比 find 快得多,您可能需要安装 icheck。
|
||||
另一种通过 inode 搜索文件的方法是使用 `debugfs` 命令。在大的文件系统上,这个命令可能比 `find` 快得多,您可能需要安装 icheck。
|
||||
|
||||
```
|
||||
$ sudo debugfs -R 'ncheck 397132' /dev/sda1
|
||||
debugfs 1.42.13 (17-May-2015)
|
||||
Inode Pathname
|
||||
397132 /home/shs/bin/runme
|
||||
|
||||
```
|
||||
|
||||
在下面的命令中,我们从主目录(〜)开始,限制搜索的深度(是我们将搜索子目录的层数),并只查看在最近一天内创建或修改的文件(mtime 设置)。
|
||||
在下面的命令中,我们从主目录(`~`)开始,限制搜索的深度(即我们将搜索子目录的层数),并只查看在最近一天内创建或修改的文件(`mtime` 设置)。
|
||||
|
||||
```
|
||||
$ find ~ -maxdepth 2 -mtime -1 -ls
|
||||
@ -132,29 +122,28 @@ $ find ~ -maxdepth 2 -mtime -1 -ls
|
||||
394006 8 -rw------- 1 shs shs 5909 Sep 21 08:18 /home/shs/.bash_history
|
||||
399612 4 -rw------- 1 shs shs 53 Sep 21 08:50 /home/shs/.Xauthority
|
||||
399615 4 drwxr-xr-x 2 shs shs 4096 Sep 21 09:32 /home/shs/Downloads
|
||||
|
||||
```
|
||||
|
||||
### 不仅仅是列出文件
|
||||
|
||||
使用 **-exec** 选项,在您使用 find 命令找到文件后可以以某种方式更改文件。您只需参照 -exec 选项即可运行相应的命令。
|
||||
使用 `-exec` 选项,在您使用 `find` 命令找到文件后可以以某种方式更改文件。您只需参照 `-exec` 选项即可运行相应的命令。
|
||||
|
||||
```
|
||||
$ find . -name runme -exec chmod 700 {} \;
|
||||
$ find . -name runme -ls
|
||||
397132 4 -rwx------ 1 shs shs 18 Sep 15 16:06 ./bin/runme
|
||||
|
||||
```
|
||||
|
||||
在这条命令中,“{}”代表文件名。此命令将更改当前目录和子目录中任何名为“runme”的文件的权限。
|
||||
在这条命令中,`{}` 代表文件名。此命令将更改当前目录和子目录中任何名为 `runme` 的文件的权限。
|
||||
|
||||
把您想运行的任何命令放在 -exec 选项之后,并使用类似于上面命令的语法即可。
|
||||
把您想运行的任何命令放在 `-exec` 选项之后,并使用类似于上面命令的语法即可。
|
||||
|
||||
### 其他搜索条件
|
||||
|
||||
如上面的例子所示,您还可以通过其他条件进行搜索:文件的修改时间、所有者、权限等。以下是一些示例。
|
||||
|
||||
#### 根据用户查找文件
|
||||
|
||||
```
|
||||
$ sudo find /home -user peanut
|
||||
/home/peanut
|
||||
@ -162,23 +151,22 @@ $ sudo find /home -user peanut
|
||||
/home/peanut/.bash_logout
|
||||
/home/peanut/.profile
|
||||
/home/peanut/examples.desktop
|
||||
|
||||
```
|
||||
|
||||
#### 根据权限查找文件
|
||||
#### 根据权限查找文件
|
||||
|
||||
```
|
||||
$ sudo find /home -perm 777
|
||||
/home/shs/whatever
|
||||
/home/oops
|
||||
|
||||
```
|
||||
|
||||
#### 根据修改时间查找文件
|
||||
|
||||
```
|
||||
$ sudo find /home -mtime +100
|
||||
/home/shs/.mozilla/firefox/krsw3giq.default/gmp-gmpopenh264/1.6/gmpopenh264.info
|
||||
/home/shs/.mozilla/firefox/krsw3giq.default/gmp-gmpopenh264/1.6/libgmpopenh264.so
|
||||
|
||||
```
|
||||
|
||||
#### 通过比较修改时间查找文件
|
||||
@ -188,12 +176,11 @@ $ sudo find /home -mtime +100
|
||||
```
|
||||
$ sudo find /var/log -newer /var/log/syslog
|
||||
/var/log/auth.log
|
||||
|
||||
```
|
||||
|
||||
### 寻找重复的文件
|
||||
|
||||
如果您正在清理磁盘空间,则可能需要删除较大的重复文件。确定文件是否真正重复的最好方法是使用 **fdupes** 命令。此命令使用 md5 校验和来确定文件是否具有相同的内容。使用 -r(递归)选项,fdupes 将在一个目录下并查找具有相同校验和而被确定为内容相同的文件。
|
||||
如果您正在清理磁盘空间,则可能需要删除较大的重复文件。确定文件是否真正重复的最好方法是使用 `fdupes` 命令。此命令使用 md5 校验和来确定文件是否具有相同的内容。使用 `-r`(递归)选项,`fdupes` 将在一个目录下并查找具有相同校验和而被确定为内容相同的文件。
|
||||
|
||||
如果以 root 身份运行这样的命令,您可能会发现很多重复的文件,但是很多文件都是创建时被添加到主目录的启动文件。
|
||||
|
||||
@ -209,25 +196,23 @@ $ sudo find /var/log -newer /var/log/syslog
|
||||
/home/tsmith/.bashrc
|
||||
/home/peanut/.bashrc
|
||||
/home/rocket/.bashrc
|
||||
|
||||
```
|
||||
|
||||
同样,您可能会在 /usr 中发现很多重复的但不该删除的配置文件。所以,请谨慎利用 fdupes 的输出。
|
||||
同样,您可能会在 `/usr` 中发现很多重复的但不该删除的配置文件。所以,请谨慎利用 `fdupes` 的输出。
|
||||
|
||||
fdupes 命令并不总是很快,但是要记住,它正在对许多文件运行校验和来做比较,你可能会意识到它的有效性。
|
||||
`fdupes` 命令并不总是很快,但是要记住,它正在对许多文件运行校验和来做比较,你可能会意识到它是多么有效。
|
||||
|
||||
### 总结
|
||||
|
||||
有很多方法可以在 Linux 系统上查找文件。如果您可以描述清楚您正在寻找什么,上面的命令将帮助您找到目标。
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.networkworld.com/article/3227075/linux/mastering-file-searches-on-linux.html
|
||||
|
||||
作者:[Sandra Henry-Stocker][a]
|
||||
译者:[jessie-pang](https://github.com/jessie-pang)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -1,13 +1,16 @@
|
||||
从命令行查看加密货币价格
|
||||
用命令行查看比特币等加密货币的价格
|
||||
======
|
||||
|
||||
![配图](https://www.ostechnix.com/wp-content/uploads/2017/11/bitcoin-1-720x340.jpg)
|
||||
前段时间,我们发布了一个关于 **[Cli-Fyi][1] ** 的指南 - 一个潜在有用的命令行查询工具。使用 Cli-Fyi,我们可以很容易地了解加密货币的最新价格和许多其他有用的细节。今天,我们将看到另一个名为 **“Coinmon”** 的加密货币价格查看工具。不像 Cli.Fyi,Coinmon 只能用来查看不同加密货币的价格。没有其他功能!Coinmon 会检查加密货币的价格,并立即直接从你的终端修改价格。它将从 [coinmarketcap.com][2] API 获取所有详细信息。对于那些 **加密货币投资者**和**工程师**来说是非常有用的。
|
||||
|
||||
前段时间,我们发布了一个关于 [Cli-Fyi][1] 的指南 - 一个可能有用的命令行查询工具。使用 Cli-Fyi,我们可以很容易地了解加密货币的最新价格和许多其他有用的细节。今天,我们将看到另一个名为 “Coinmon” 的加密货币价格查看工具。不像 Cli.Fyi,Coinmon 只能用来查看不同加密货币的价格。没有其他功能!Coinmon 会在终端上检查加密货币的价格。它将从 [coinmarketcap.com][2] API 获取所有详细信息。对于那些 **加密货币投资者**和**工程师**来说是非常有用的。
|
||||
|
||||
### 安装 Coinmon
|
||||
|
||||
确保你的系统上安装了 Node.js 和 Npm。如果你的机器上没有安装 Node.js 和/或 npm,请参考以下链接进行安装。
|
||||
|
||||
安装完 Node.js 和 Npm 后,从终端运行以下命令安装 Coinmon。
|
||||
|
||||
```
|
||||
sudo npm install -g coinmon
|
||||
```
|
||||
@ -15,38 +18,42 @@ sudo npm install -g coinmon
|
||||
### 从命令行查看加密货币价格
|
||||
|
||||
运行以下命令查看市值排名的前 10 位的加密货币:
|
||||
|
||||
```
|
||||
coinmon
|
||||
```
|
||||
|
||||
示例输出:
|
||||
|
||||
[![][3]][4]
|
||||
![][4]
|
||||
|
||||
如我所说,如果你不带任何参数运行 Coinmon,它将显示前 10 位加密货币。你还可以使用 `-t` 标志查看最高的 n 位加密货币,例如 20。
|
||||
|
||||
如我所说,如果你不带任何参数运行 coinmon,它将显示前 10 位加密货币。你还可以使用 “-t” 标志查看最高的 n 位加密货币,例如 20。
|
||||
```
|
||||
coinmon -t 20
|
||||
```
|
||||
|
||||
所有价格默认以美元显示。你还可以使用 “-c” 标志将价格从美元转换为另一种货币。
|
||||
所有价格默认以美元显示。你还可以使用 `-c` 标志将价格从美元转换为另一种货币。
|
||||
|
||||
例如,要将价格转换为 INR(印度卢比),运行:
|
||||
|
||||
```
|
||||
coinmon -c inr
|
||||
```
|
||||
|
||||
[![][3]][5]
|
||||
![][5]
|
||||
|
||||
目前,Coinmon 支持 AUD、BRL、CAD、CHF、CLP、CNY、CZK、DKK、EUR、GBP、HKD、HUF、IDR、ILS、INR、JPY、KRW、MXN、MYR、NOK、NZD、PHP、PKR、PLN、RUB、SEK、SGD、THB、TRY、TWD、ZAR 这些货币。
|
||||
|
||||
也可以使用加密货币的符号来搜索价格。
|
||||
|
||||
```
|
||||
coinmon -f btc
|
||||
```
|
||||
|
||||
这里,**btc** 是比特币的符号。你可以在[**这**][6]查看所有可用的加密货币的符号。
|
||||
这里,`btc` 是比特币的符号。你可以在[**这里**][6]查看所有可用的加密货币的符号。
|
||||
|
||||
有关更多详情,请参阅coinmon的帮助部分:
|
||||
有关更多详情,请参阅 coinmon 的帮助部分:
|
||||
|
||||
```
|
||||
$ coinmon -h
|
||||
@ -67,15 +74,13 @@ Options:
|
||||
|
||||
干杯!
|
||||
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.ostechnix.com/coinmon-check-cryptocurrency-prices-commandline/
|
||||
|
||||
作者:[SK][a]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
@ -83,6 +88,6 @@ via: https://www.ostechnix.com/coinmon-check-cryptocurrency-prices-commandline/
|
||||
[1]:https://www.ostechnix.com/cli-fyi-quick-easy-way-fetch-information-ips-emails-domains-lots/
|
||||
[2]:https://coinmarketcap.com/
|
||||
[3]:data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
|
||||
[4]:http://www.ostechnix.com/wp-content/uploads/2017/11/coinmon-1.png ()
|
||||
[5]:http://www.ostechnix.com/wp-content/uploads/2017/11/coinmon-2.png ()
|
||||
[4]:http://www.ostechnix.com/wp-content/uploads/2017/11/coinmon-1.png
|
||||
[5]:http://www.ostechnix.com/wp-content/uploads/2017/11/coinmon-2.png
|
||||
[6]:https://en.wikipedia.org/wiki/List_of_cryptocurrencies
|
@ -1,342 +0,0 @@
|
||||
BriFuture is Translating this article
|
||||
|
||||
Let’s Build A Simple Interpreter. Part 3.
|
||||
======
|
||||
|
||||
I woke up this morning and I thought to myself: "Why do we find it so difficult to learn a new skill?"
|
||||
|
||||
I don't think it's just because of the hard work. I think that one of the reasons might be that we spend a lot of time and hard work acquiring knowledge by reading and watching and not enough time translating that knowledge into a skill by practicing it. Take swimming, for example. You can spend a lot of time reading hundreds of books about swimming, talk for hours with experienced swimmers and coaches, watch all the training videos available, and you still will sink like a rock the first time you jump in the pool.
|
||||
|
||||
The bottom line is: it doesn't matter how well you think you know the subject - you have to put that knowledge into practice to turn it into a skill. To help you with the practice part I put exercises into [Part 1][1] and [Part 2][2] of the series. And yes, you will see more exercises in today's article and in future articles, I promise :)
|
||||
|
||||
Okay, let's get started with today's material, shall we?
|
||||
|
||||
|
||||
So far, you've learned how to interpret arithmetic expressions that add or subtract two integers like "7 + 3" or "12 - 9". Today I'm going to talk about how to parse (recognize) and interpret arithmetic expressions that have any number of plus or minus operators in it, for example "7 - 3 + 2 - 1".
|
||||
|
||||
Graphically, the arithmetic expressions in this article can be represented with the following syntax diagram:
|
||||
|
||||
![][3]
|
||||
|
||||
What is a syntax diagram? A **syntax diagram** is a graphical representation of a programming language 's syntax rules. Basically, a syntax diagram visually shows you which statements are allowed in your programming language and which are not.
|
||||
|
||||
Syntax diagrams are pretty easy to read: just follow the paths indicated by the arrows. Some paths indicate choices. And some paths indicate loops.
|
||||
|
||||
You can read the above syntax diagram as following: a term optionally followed by a plus or minus sign, followed by another term, which in turn is optionally followed by a plus or minus sign followed by another term and so on. You get the picture, literally. You might wonder what a "term" is. For the purpose of this article a "term" is just an integer.
|
||||
|
||||
Syntax diagrams serve two main purposes:
|
||||
|
||||
* They graphically represent the specification (grammar) of a programming language.
|
||||
* They can be used to help you write your parser - you can map a diagram to code by following simple rules.
|
||||
|
||||
|
||||
|
||||
You've learned that the process of recognizing a phrase in the stream of tokens is called **parsing**. And the part of an interpreter or compiler that performs that job is called a **parser**. Parsing is also called **syntax analysis** , and the parser is also aptly called, you guessed it right, a **syntax analyzer**.
|
||||
|
||||
According to the syntax diagram above, all of the following arithmetic expressions are valid:
|
||||
|
||||
* 3
|
||||
* 3 + 4
|
||||
* 7 - 3 + 2 - 1
|
||||
|
||||
|
||||
|
||||
Because syntax rules for arithmetic expressions in different programming languages are very similar we can use a Python shell to "test" our syntax diagram. Launch your Python shell and see for yourself:
|
||||
```
|
||||
>>> 3
|
||||
3
|
||||
>>> 3 + 4
|
||||
7
|
||||
>>> 7 - 3 + 2 - 1
|
||||
5
|
||||
```
|
||||
|
||||
No surprises here.
|
||||
|
||||
The expression "3 + " is not a valid arithmetic expression though because according to the syntax diagram the plus sign must be followed by a term (integer), otherwise it's a syntax error. Again, try it with a Python shell and see for yourself:
|
||||
```
|
||||
>>> 3 +
|
||||
File "<stdin>", line 1
|
||||
3 +
|
||||
^
|
||||
SyntaxError: invalid syntax
|
||||
```
|
||||
|
||||
It's great to be able to use a Python shell to do some testing but let's map the above syntax diagram to code and use our own interpreter for testing, all right?
|
||||
|
||||
You know from the previous articles ([Part 1][1] and [Part 2][2]) that the expr method is where both our parser and interpreter live. Again, the parser just recognizes the structure making sure that it corresponds to some specifications and the interpreter actually evaluates the expression once the parser has successfully recognized (parsed) it.
|
||||
|
||||
The following code snippet shows the parser code corresponding to the diagram. The rectangular box from the syntax diagram (term) becomes a term method that parses an integer and the expr method just follows the syntax diagram flow:
|
||||
```
|
||||
def term(self):
|
||||
self.eat(INTEGER)
|
||||
|
||||
def expr(self):
|
||||
# set current token to the first token taken from the input
|
||||
self.current_token = self.get_next_token()
|
||||
|
||||
self.term()
|
||||
while self.current_token.type in (PLUS, MINUS):
|
||||
token = self.current_token
|
||||
if token.type == PLUS:
|
||||
self.eat(PLUS)
|
||||
self.term()
|
||||
elif token.type == MINUS:
|
||||
self.eat(MINUS)
|
||||
self.term()
|
||||
```
|
||||
|
||||
You can see that expr first calls the term method. Then the expr method has a while loop which can execute zero or more times. And inside the loop the parser makes a choice based on the token (whether it's a plus or minus sign). Spend some time proving to yourself that the code above does indeed follow the syntax diagram flow for arithmetic expressions.
|
||||
|
||||
The parser itself does not interpret anything though: if it recognizes an expression it's silent and if it doesn't, it throws out a syntax error. Let's modify the expr method and add the interpreter code:
|
||||
```
|
||||
def term(self):
|
||||
"""Return an INTEGER token value"""
|
||||
token = self.current_token
|
||||
self.eat(INTEGER)
|
||||
return token.value
|
||||
|
||||
def expr(self):
|
||||
"""Parser / Interpreter """
|
||||
# set current token to the first token taken from the input
|
||||
self.current_token = self.get_next_token()
|
||||
|
||||
result = self.term()
|
||||
while self.current_token.type in (PLUS, MINUS):
|
||||
token = self.current_token
|
||||
if token.type == PLUS:
|
||||
self.eat(PLUS)
|
||||
result = result + self.term()
|
||||
elif token.type == MINUS:
|
||||
self.eat(MINUS)
|
||||
result = result - self.term()
|
||||
|
||||
return result
|
||||
```
|
||||
|
||||
Because the interpreter needs to evaluate an expression the term method was modified to return an integer value and the expr method was modified to perform addition and subtraction at the appropriate places and return the result of interpretation. Even though the code is pretty straightforward I recommend spending some time studying it.
|
||||
|
||||
Le's get moving and see the complete code of the interpreter now, okay?
|
||||
|
||||
Here is the source code for your new version of the calculator that can handle valid arithmetic expressions containing integers and any number of addition and subtraction operators:
|
||||
```
|
||||
# Token types
|
||||
#
|
||||
# EOF (end-of-file) token is used to indicate that
|
||||
# there is no more input left for lexical analysis
|
||||
INTEGER, PLUS, MINUS, EOF = 'INTEGER', 'PLUS', 'MINUS', 'EOF'
|
||||
|
||||
|
||||
class Token(object):
|
||||
def __init__(self, type, value):
|
||||
# token type: INTEGER, PLUS, MINUS, or EOF
|
||||
self.type = type
|
||||
# token value: non-negative integer value, '+', '-', or None
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
"""String representation of the class instance.
|
||||
|
||||
Examples:
|
||||
Token(INTEGER, 3)
|
||||
Token(PLUS, '+')
|
||||
"""
|
||||
return 'Token({type}, {value})'.format(
|
||||
type=self.type,
|
||||
value=repr(self.value)
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
|
||||
|
||||
class Interpreter(object):
|
||||
def __init__(self, text):
|
||||
# client string input, e.g. "3 + 5", "12 - 5 + 3", etc
|
||||
self.text = text
|
||||
# self.pos is an index into self.text
|
||||
self.pos = 0
|
||||
# current token instance
|
||||
self.current_token = None
|
||||
self.current_char = self.text[self.pos]
|
||||
|
||||
##########################################################
|
||||
# Lexer code #
|
||||
##########################################################
|
||||
def error(self):
|
||||
raise Exception('Invalid syntax')
|
||||
|
||||
def advance(self):
|
||||
"""Advance the `pos` pointer and set the `current_char` variable."""
|
||||
self.pos += 1
|
||||
if self.pos > len(self.text) - 1:
|
||||
self.current_char = None # Indicates end of input
|
||||
else:
|
||||
self.current_char = self.text[self.pos]
|
||||
|
||||
def skip_whitespace(self):
|
||||
while self.current_char is not None and self.current_char.isspace():
|
||||
self.advance()
|
||||
|
||||
def integer(self):
|
||||
"""Return a (multidigit) integer consumed from the input."""
|
||||
result = ''
|
||||
while self.current_char is not None and self.current_char.isdigit():
|
||||
result += self.current_char
|
||||
self.advance()
|
||||
return int(result)
|
||||
|
||||
def get_next_token(self):
|
||||
"""Lexical analyzer (also known as scanner or tokenizer)
|
||||
|
||||
This method is responsible for breaking a sentence
|
||||
apart into tokens. One token at a time.
|
||||
"""
|
||||
while self.current_char is not None:
|
||||
|
||||
if self.current_char.isspace():
|
||||
self.skip_whitespace()
|
||||
continue
|
||||
|
||||
if self.current_char.isdigit():
|
||||
return Token(INTEGER, self.integer())
|
||||
|
||||
if self.current_char == '+':
|
||||
self.advance()
|
||||
return Token(PLUS, '+')
|
||||
|
||||
if self.current_char == '-':
|
||||
self.advance()
|
||||
return Token(MINUS, '-')
|
||||
|
||||
self.error()
|
||||
|
||||
return Token(EOF, None)
|
||||
|
||||
##########################################################
|
||||
# Parser / Interpreter code #
|
||||
##########################################################
|
||||
def eat(self, token_type):
|
||||
# compare the current token type with the passed token
|
||||
# type and if they match then "eat" the current token
|
||||
# and assign the next token to the self.current_token,
|
||||
# otherwise raise an exception.
|
||||
if self.current_token.type == token_type:
|
||||
self.current_token = self.get_next_token()
|
||||
else:
|
||||
self.error()
|
||||
|
||||
def term(self):
|
||||
"""Return an INTEGER token value."""
|
||||
token = self.current_token
|
||||
self.eat(INTEGER)
|
||||
return token.value
|
||||
|
||||
def expr(self):
|
||||
"""Arithmetic expression parser / interpreter."""
|
||||
# set current token to the first token taken from the input
|
||||
self.current_token = self.get_next_token()
|
||||
|
||||
result = self.term()
|
||||
while self.current_token.type in (PLUS, MINUS):
|
||||
token = self.current_token
|
||||
if token.type == PLUS:
|
||||
self.eat(PLUS)
|
||||
result = result + self.term()
|
||||
elif token.type == MINUS:
|
||||
self.eat(MINUS)
|
||||
result = result - self.term()
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
while True:
|
||||
try:
|
||||
# To run under Python3 replace 'raw_input' call
|
||||
# with 'input'
|
||||
text = raw_input('calc> ')
|
||||
except EOFError:
|
||||
break
|
||||
if not text:
|
||||
continue
|
||||
interpreter = Interpreter(text)
|
||||
result = interpreter.expr()
|
||||
print(result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
```
|
||||
|
||||
Save the above code into the calc3.py file or download it directly from [GitHub][4]. Try it out. See for yourself that it can handle arithmetic expressions that you can derive from the syntax diagram I showed you earlier.
|
||||
|
||||
Here is a sample session that I ran on my laptop:
|
||||
```
|
||||
$ python calc3.py
|
||||
calc> 3
|
||||
3
|
||||
calc> 7 - 4
|
||||
3
|
||||
calc> 10 + 5
|
||||
15
|
||||
calc> 7 - 3 + 2 - 1
|
||||
5
|
||||
calc> 10 + 1 + 2 - 3 + 4 + 6 - 15
|
||||
5
|
||||
calc> 3 +
|
||||
Traceback (most recent call last):
|
||||
File "calc3.py", line 147, in <module>
|
||||
main()
|
||||
File "calc3.py", line 142, in main
|
||||
result = interpreter.expr()
|
||||
File "calc3.py", line 123, in expr
|
||||
result = result + self.term()
|
||||
File "calc3.py", line 110, in term
|
||||
self.eat(INTEGER)
|
||||
File "calc3.py", line 105, in eat
|
||||
self.error()
|
||||
File "calc3.py", line 45, in error
|
||||
raise Exception('Invalid syntax')
|
||||
Exception: Invalid syntax
|
||||
```
|
||||
|
||||
|
||||
Remember those exercises I mentioned at the beginning of the article: here they are, as promised :)
|
||||
|
||||
![][5]
|
||||
|
||||
* Draw a syntax diagram for arithmetic expressions that contain only multiplication and division, for example "7 0_sync_master.sh 1_add_new_article_manual.sh 1_add_new_article_newspaper.sh 2_start_translating.sh 3_continue_the_work.sh 4_finish.sh 5_pause.sh base.sh env format.test lctt.cfg parse_url_by_manual.sh parse_url_by_newspaper.py parse_url_by_newspaper.sh README.org reformat.sh 4 / 2 0_sync_master.sh 1_add_new_article_manual.sh 1_add_new_article_newspaper.sh 2_start_translating.sh 3_continue_the_work.sh 4_finish.sh 5_pause.sh base.sh env format.test lctt.cfg parse_url_by_manual.sh parse_url_by_newspaper.py parse_url_by_newspaper.sh README.org reformat.sh 3". Seriously, just grab a pen or a pencil and try to draw one.
|
||||
* Modify the source code of the calculator to interpret arithmetic expressions that contain only multiplication and division, for example "7 0_sync_master.sh 1_add_new_article_manual.sh 1_add_new_article_newspaper.sh 2_start_translating.sh 3_continue_the_work.sh 4_finish.sh 5_pause.sh base.sh env format.test lctt.cfg parse_url_by_manual.sh parse_url_by_newspaper.py parse_url_by_newspaper.sh README.org reformat.sh 4 / 2 * 3".
|
||||
* Write an interpreter that handles arithmetic expressions like "7 - 3 + 2 - 1" from scratch. Use any programming language you're comfortable with and write it off the top of your head without looking at the examples. When you do that, think about components involved: a lexer that takes an input and converts it into a stream of tokens, a parser that feeds off the stream of the tokens provided by the lexer and tries to recognize a structure in that stream, and an interpreter that generates results after the parser has successfully parsed (recognized) a valid arithmetic expression. String those pieces together. Spend some time translating the knowledge you've acquired into a working interpreter for arithmetic expressions.
|
||||
|
||||
|
||||
|
||||
**Check your understanding.**
|
||||
|
||||
1. What is a syntax diagram?
|
||||
2. What is syntax analysis?
|
||||
3. What is a syntax analyzer?
|
||||
|
||||
|
||||
|
||||
|
||||
Hey, look! You read all the way to the end. Thanks for hanging out here today and don't forget to do the exercises. :) I'll be back next time with a new article - stay tuned.
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://ruslanspivak.com/lsbasi-part3/
|
||||
|
||||
作者:[Ruslan Spivak][a]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://ruslanspivak.com
|
||||
[1]:http://ruslanspivak.com/lsbasi-part1/ (Part 1)
|
||||
[2]:http://ruslanspivak.com/lsbasi-part2/ (Part 2)
|
||||
[3]:https://ruslanspivak.com/lsbasi-part3/lsbasi_part3_syntax_diagram.png
|
||||
[4]:https://github.com/rspivak/lsbasi/blob/master/part3/calc3.py
|
||||
[5]:https://ruslanspivak.com/lsbasi-part3/lsbasi_part3_exercises.png
|
@ -0,0 +1,336 @@
|
||||
让我们做个简单的解释器(3)
|
||||
======
|
||||
|
||||
早上醒来的时候,我就在想:“为什么我们学习一个新技能这么难?”
|
||||
|
||||
我不认为那是因为它很难。我认为原因可能在于我们花了太多的时间,而这件难事需要有丰富的阅历和足够的知识,然而我们要把这样的知识转换成技能所用的练习时间又不够。
|
||||
拿游泳来说,你可以花上几天时间来阅读很多有关游泳的书籍,花几个小时和资深的游泳者和教练交流,观看所有可以获得的训练视频,但你第一次跳进水池的时候,仍然会像一个石头那样沉入水中,
|
||||
|
||||
|
||||
要点在于:你认为自己有多了解那件事都无关紧要 —— 你得通过练习把知识变成技能。为了帮你练习,我把训练放在了这个系列的 [第一部分][1] 和 [第二部分][2] 了。当然,你会在今后的文章中看到更多练习,我保证 :)
|
||||
|
||||
好,让我们开始今天的学习。
|
||||
|
||||
到现在为止,你已经知道了怎样解释像 “7 + 3” 或者 “12 - 9” 这样的两个整数相加减的算术表达式。今天我要说的是怎么解析(识别)、解释有多个数字相加减的算术表达式,比如 “7 - 3 + 2 - 1”。
|
||||
|
||||
文中的这个算术表达式可以用下面的这个语法图表示:
|
||||
|
||||
![][3]
|
||||
|
||||
什么是语法图? **语法图** 是对一门编程语言中的语法规则进行图像化的表示。基本上,一个语法图就能告诉你哪些语句可以在程序中出现,哪些不能出现。
|
||||
|
||||
语法图很容易读懂:按照箭头指向的路径。某些路径表示的是判断,有些表示的是循环。
|
||||
|
||||
你可以按照以下的方式读上面的语法图:一个 term 后面可以是加号或者减号,接着可以是另一个 term,这个 term 后面又可以是一个加号或者减号,后面又是一个 term,如此循环。从字面上你就能读懂这个图片了。或许你会奇怪,“term” 是什么、对于本文来说,“term” 就是个整数。
|
||||
|
||||
语法图有两个主要的作用:
|
||||
|
||||
* 它们用图形的方式表示一个编程语言的特性(语法)。
|
||||
* 它们可以用来帮你写出解析器 —— 你可以根据下列简单规则把图片转换成代码。
|
||||
|
||||
|
||||
|
||||
你已经知道,识别出记号流中的词组的过程就叫做 **解析**。解释器或者编译器执行这个任务的部分叫做 **解析器**。解析也称为 **语法分析**,并且解析器这个名字很合适,你猜的对,就是 **语法分析**。
|
||||
|
||||
根据上面的语法图,下面这些表达式都是合法的:
|
||||
|
||||
* 3
|
||||
* 3 + 4
|
||||
* 7 - 3 + 2 - 1
|
||||
|
||||
|
||||
|
||||
因为算术表达式的语法规则在不同的编程语言里面是很相近的,我们可以用 Python shell 来“测试”语法图。打开 Python shell,运行下面的代码:
|
||||
```
|
||||
>>> 3
|
||||
3
|
||||
>>> 3 + 4
|
||||
7
|
||||
>>> 7 - 3 + 2 - 1
|
||||
5
|
||||
```
|
||||
|
||||
意料之中。
|
||||
|
||||
表达式 “3 + ” 不是一个有效的数学表达式,根据语法图,加号后面必须要有个 term (整数),否则就是语法错误。然后,自己在 Python shell 里面运行:
|
||||
```
|
||||
>>> 3 +
|
||||
File "<stdin>", line 1
|
||||
3 +
|
||||
^
|
||||
SyntaxError: invalid syntax
|
||||
```
|
||||
|
||||
能用 Python shell 来做这样的测试非常棒,让我们把上面的语法图转换成代码,用我们自己的解释器来测试,怎么样?
|
||||
|
||||
从之前的文章里([第一部分][1] 和 [第二部分][2])你知道 expr 方法包含了我们的解析器和解释器。再说一遍,解析器仅仅识别出结构,确保它与某些特性对应,而解释器实际上是在解析器成功识别(解析)特性之后,就立即对表达式进行评估。
|
||||
|
||||
以下代码片段显示了对应于图表的解析器代码。语法图里面的矩形方框(term)变成了 term 方法,用于解析整数,expr 方法和语法图的流程一致:
|
||||
```
|
||||
def term(self):
|
||||
self.eat(INTEGER)
|
||||
|
||||
def expr(self):
|
||||
# 把当前标记设为从输入中拿到的第一个标记
|
||||
self.current_token = self.get_next_token()
|
||||
|
||||
self.term()
|
||||
while self.current_token.type in (PLUS, MINUS):
|
||||
token = self.current_token
|
||||
if token.type == PLUS:
|
||||
self.eat(PLUS)
|
||||
self.term()
|
||||
elif token.type == MINUS:
|
||||
self.eat(MINUS)
|
||||
self.term()
|
||||
```
|
||||
|
||||
你能看到 expr 首先调用了 term 方法。然后 expr 方法里面的 while 循环可以执行 0 或多次。在循环里面解析器基于标记做出判断(是加号还是减号)。花一些时间,你就知道,上述代码确实是遵循着语法图的算术表达式流程。
|
||||
|
||||
解析器并不解释任何东西:如果它识别出了一个表达式,它就静默着,如果没有识别出来,就会抛出一个语法错误。改一下 expr 方法,加入解释器的代码:
|
||||
```
|
||||
def term(self):
|
||||
"""Return an INTEGER token value"""
|
||||
token = self.current_token
|
||||
self.eat(INTEGER)
|
||||
return token.value
|
||||
|
||||
def expr(self):
|
||||
"""Parser / Interpreter """
|
||||
# 将输入中的第一个标记设置成当前标记
|
||||
self.current_token = self.get_next_token()
|
||||
|
||||
result = self.term()
|
||||
while self.current_token.type in (PLUS, MINUS):
|
||||
token = self.current_token
|
||||
if token.type == PLUS:
|
||||
self.eat(PLUS)
|
||||
result = result + self.term()
|
||||
elif token.type == MINUS:
|
||||
self.eat(MINUS)
|
||||
result = result - self.term()
|
||||
|
||||
return result
|
||||
```
|
||||
|
||||
因为解释器需要评估一个表达式, term 方法被改成返回一个整型值,expr 方法被改成在合适的地方执行加法或减法操作,并返回解释的结果。尽管代码很直白,我建议花点时间去理解它。
|
||||
进行下一步,看看完整的解释器代码,好不?
|
||||
|
||||
这时新版计算器的源代码,它可以处理包含有任意多个加法和减法运算的有效的数学表达式。
|
||||
```
|
||||
# 标记类型
|
||||
#
|
||||
# EOF (end-of-file 文件末尾) 标记是用来表示所有输入都解析完成
|
||||
INTEGER, PLUS, MINUS, EOF = 'INTEGER', 'PLUS', 'MINUS', 'EOF'
|
||||
|
||||
|
||||
class Token(object):
|
||||
def __init__(self, type, value):
|
||||
# token 类型: INTEGER, PLUS, MINUS, or EOF
|
||||
self.type = type
|
||||
# token 值: 非负整数值, '+', '-', 或无
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
"""String representation of the class instance.
|
||||
|
||||
Examples:
|
||||
Token(INTEGER, 3)
|
||||
Token(PLUS, '+')
|
||||
"""
|
||||
return 'Token({type}, {value})'.format(
|
||||
type=self.type,
|
||||
value=repr(self.value)
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
|
||||
|
||||
class Interpreter(object):
|
||||
def __init__(self, text):
|
||||
# 客户端字符输入, 例如. "3 + 5", "12 - 5",
|
||||
self.text = text
|
||||
# self.pos is an index into self.text
|
||||
self.pos = 0
|
||||
# 当前标记实例
|
||||
self.current_token = None
|
||||
self.current_char = self.text[self.pos]
|
||||
|
||||
##########################################################
|
||||
# Lexer code #
|
||||
##########################################################
|
||||
def error(self):
|
||||
raise Exception('Invalid syntax')
|
||||
|
||||
def advance(self):
|
||||
"""Advance the `pos` pointer and set the `current_char` variable."""
|
||||
self.pos += 1
|
||||
if self.pos > len(self.text) - 1:
|
||||
self.current_char = None # Indicates end of input
|
||||
else:
|
||||
self.current_char = self.text[self.pos]
|
||||
|
||||
def skip_whitespace(self):
|
||||
while self.current_char is not None and self.current_char.isspace():
|
||||
self.advance()
|
||||
|
||||
def integer(self):
|
||||
"""Return a (multidigit) integer consumed from the input."""
|
||||
result = ''
|
||||
while self.current_char is not None and self.current_char.isdigit():
|
||||
result += self.current_char
|
||||
self.advance()
|
||||
return int(result)
|
||||
|
||||
def get_next_token(self):
|
||||
"""Lexical analyzer (also known as scanner or tokenizer)
|
||||
|
||||
This method is responsible for breaking a sentence
|
||||
apart into tokens. One token at a time.
|
||||
"""
|
||||
while self.current_char is not None:
|
||||
|
||||
if self.current_char.isspace():
|
||||
self.skip_whitespace()
|
||||
continue
|
||||
|
||||
if self.current_char.isdigit():
|
||||
return Token(INTEGER, self.integer())
|
||||
|
||||
if self.current_char == '+':
|
||||
self.advance()
|
||||
return Token(PLUS, '+')
|
||||
|
||||
if self.current_char == '-':
|
||||
self.advance()
|
||||
return Token(MINUS, '-')
|
||||
|
||||
self.error()
|
||||
|
||||
return Token(EOF, None)
|
||||
|
||||
##########################################################
|
||||
# Parser / Interpreter code #
|
||||
##########################################################
|
||||
def eat(self, token_type):
|
||||
# 将当前的标记类型与传入的标记类型作比较,如果他们相匹配,就
|
||||
# “eat” 掉当前的标记并将下一个标记赋给 self.current_token,
|
||||
# 否则抛出一个异常
|
||||
if self.current_token.type == token_type:
|
||||
self.current_token = self.get_next_token()
|
||||
else:
|
||||
self.error()
|
||||
|
||||
def term(self):
|
||||
"""Return an INTEGER token value."""
|
||||
token = self.current_token
|
||||
self.eat(INTEGER)
|
||||
return token.value
|
||||
|
||||
def expr(self):
|
||||
"""Arithmetic expression parser / interpreter."""
|
||||
# 将输入中的第一个标记设置成当前标记
|
||||
self.current_token = self.get_next_token()
|
||||
|
||||
result = self.term()
|
||||
while self.current_token.type in (PLUS, MINUS):
|
||||
token = self.current_token
|
||||
if token.type == PLUS:
|
||||
self.eat(PLUS)
|
||||
result = result + self.term()
|
||||
elif token.type == MINUS:
|
||||
self.eat(MINUS)
|
||||
result = result - self.term()
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
while True:
|
||||
try:
|
||||
# To run under Python3 replace 'raw_input' call
|
||||
# 要在 Python3 下运行,请把 ‘raw_input’ 的调用换成 ‘input’
|
||||
text = raw_input('calc> ')
|
||||
except EOFError:
|
||||
break
|
||||
if not text:
|
||||
continue
|
||||
interpreter = Interpreter(text)
|
||||
result = interpreter.expr()
|
||||
print(result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
```
|
||||
|
||||
把上面的代码保存到 calc3.py 文件中,或者直接从 [GitHub][4] 上下载。试着运行它。看看它能不能处理我之前给你看过的语法图里面派生出的数学表达式。
|
||||
|
||||
这是我在自己的笔记本上运行的示例:
|
||||
```
|
||||
$ python calc3.py
|
||||
calc> 3
|
||||
3
|
||||
calc> 7 - 4
|
||||
3
|
||||
calc> 10 + 5
|
||||
15
|
||||
calc> 7 - 3 + 2 - 1
|
||||
5
|
||||
calc> 10 + 1 + 2 - 3 + 4 + 6 - 15
|
||||
5
|
||||
calc> 3 +
|
||||
Traceback (most recent call last):
|
||||
File "calc3.py", line 147, in <module>
|
||||
main()
|
||||
File "calc3.py", line 142, in main
|
||||
result = interpreter.expr()
|
||||
File "calc3.py", line 123, in expr
|
||||
result = result + self.term()
|
||||
File "calc3.py", line 110, in term
|
||||
self.eat(INTEGER)
|
||||
File "calc3.py", line 105, in eat
|
||||
self.error()
|
||||
File "calc3.py", line 45, in error
|
||||
raise Exception('Invalid syntax')
|
||||
Exception: Invalid syntax
|
||||
```
|
||||
|
||||
|
||||
记得我在文章开始时提过的练习吗:他们在这儿,我保证过的:)
|
||||
|
||||
![][5]
|
||||
|
||||
* 画出只包含乘法和除法的数学表达式的语法图,比如 “7 * 4 / 2 * 3”。认真点,拿只钢笔或铅笔,试着画一个。
|
||||
修改计算器的源代码,解释只包含乘法和除法的数学表达式。比如 “7 * 4 / 2 * 3”。
|
||||
* 从头写一个可以处理像 “7 - 3 + 2 - 1” 这样的数学表达式的解释器。用你熟悉的编程语言,不看示例代码自己思考着写出代码。做的时候要想一想这里面包含的组件:一个 lexer,读取输入并转换成标记流,一个解析器,从 lexer 提供的记号流中取食,并且尝试识别流中的结构,一个解释器,在解析器成功解析(识别)有效的数学表达式后产生结果。把这些要点串起来。花一点时间把你获得的知识变成一个可以运行的数学表达式的解释器。
|
||||
|
||||
|
||||
**检验你的理解。**
|
||||
|
||||
1. 什么是语法图?
|
||||
2. 什么是语法分析?
|
||||
3. 什么是语法分析器?
|
||||
|
||||
|
||||
|
||||
|
||||
嘿,看!你看完了所有内容。感谢你们坚持到今天,而且没有忘记练习。:) 下次我会带着新的文章回来,尽请期待。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://ruslanspivak.com/lsbasi-part3/
|
||||
|
||||
作者:[Ruslan Spivak][a]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://ruslanspivak.com
|
||||
[1]:http://ruslanspivak.com/lsbasi-part1/ (Part 1)
|
||||
[2]:http://ruslanspivak.com/lsbasi-part2/ (Part 2)
|
||||
[3]:https://ruslanspivak.com/lsbasi-part3/lsbasi_part3_syntax_diagram.png
|
||||
[4]:https://github.com/rspivak/lsbasi/blob/master/part3/calc3.py
|
||||
[5]:https://ruslanspivak.com/lsbasi-part3/lsbasi_part3_exercises.png
|
Loading…
Reference in New Issue
Block a user