Skip to content

Commit

Permalink
Merge pull request goreliu#23 from AllanDowney/master
Browse files Browse the repository at this point in the history
add zsh style for code block
  • Loading branch information
goreliu authored May 5, 2022
2 parents 768b7ac + 9ab663c commit aac8b3c
Show file tree
Hide file tree
Showing 21 changed files with 464 additions and 462 deletions.
67 changes: 34 additions & 33 deletions 01_Zsh-开发指南(第一篇-变量和语句).md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

第一个是 zsh 的部署成本。但因为多数情况都需要部署其他软件,甚至自己的脚本可以和 zsh 打包部署(去掉用不到的文件后的 zsh 只有 1M 多),所以基本不成问题。而且如果使用系统默认的 bash 的话,还涉及版本不同导致的问题,比如不同系统的 bash 版本不一样,或者系统升级后,bash 的升级导致之前的脚本挂掉等等。所以即使使用 bash,最好也是统一部署或者自带一个特定的版本,而不是使用系统默认的,以减少不必要的麻烦。

第二个就是非常重要的学习成本。因为会写 bash 的人很多,但会写 zsh 的比较少,如果只有自己会写,那么和别人合作会出问题。但 zsh 的学习成本并没有那么大,尤其是对会 bash 开发者来说,要大致看懂 zsh 脚本基本只需要几十分钟的学习,而编写的话,循序渐进也是很自然的事情,而且想不起来的时候还可以用 bash 的语法写。所以学习成本没有那么可观。
第二个就是非常重要的学习成本。因为会写 bash 的人很多,但会写 zsh 的比较少,如果只有自己会写,那么和别人合作会出问题。但 zsh 的学习成本并没有那么大,尤其是对会 bash 开发者来说,要大致看懂 zsh 脚本基本只需要几十分钟的学习,而编写的话,循序渐进也是很自然的事情,而且想不起来的时候还可以用 bash 的语法写。所以学习成本没有那么可观。

第三个是使用 zsh 开发的好处。如果 zsh 和 bash 相比,没有明显的好处,为什么要学习和使用它呢?那么就要从 bash 痛点讲起了。我想经常写 bash 脚本的人,很少有人会举大拇指说 bash 真好用啊。相反,我曾经多次听某些开发者说我写过一个超过 2000(或者其他行数)行的 shell(bash)脚本。但几乎没有人会认为写一个超过 2000 行的 Python 脚本是一件多么特别的事情。蹩脚的语法(几乎所有从任何其他语言迁移过来的开发者,都要重新熟悉和习惯它的语法)、严重依赖外部命令(因为文件系统错误等问题,挂掉一个外部命令,脚本就休克了。命令版本不同会有用法上的微秒差别,调试测试困难。频繁起新进程性能低下)、功能孱弱蹩脚(很多需要频繁使用的功能不全面或者不好用,比如字符串处理和数组的用法)等等,让很多开发者非常头疼,其中有些人甚至主张禁止使用 shell 脚本,一律改用 Python 等等,但 Python 并非适用所有场景,而且也有另外的一些问题,这样做也是因噎废食。Zsh 并非将这些问题全部解决了,但和 bash 相比,有很大的改善。比如 zsh 支持多种风格的语法,开发者很容易找到亲切感;对外部命令的依赖比 bash 要轻很多,多数常用的功能不需要使用外部命令,性能更好,调试也更加方便;功能上和 bash 相比也有比较大的提升,处理不那么复杂的场景已经比较够用了。

Expand All @@ -34,7 +34,7 @@

可以通过一个例子直观感受下用 zsh 写的脚本。这是一个删除当前目录以及所有子目录下重复文件的脚本,通过 md5 判断文件是否相同(不严谨)。熟悉 bash 的读者可以尝试用 bash 完成相同的功能,然后对比一下代码(我之前写过一个 bash 版本的,不贴上来了),就能比较直观地感受到 bash 和 zsh 的区别了。

```
```zsh
#!/bin/zsh

local files=("${(f)$(md5sum **/*(.D))}")
Expand All @@ -60,7 +60,7 @@ for i ($files) {
那么要从 shell 脚本的使用场景说起。Shell 是一种和计算机系统交互的文本界面(CLI),简单说就是输入命令后返回结果(也有比较复杂的操作)。CLI 在某些场景要比图形界面(GUI)方便和高效很多,是不可取代的(即使有一天语音识别取代了文本输入,CLI 也会换汤不换药地继续存在)。那么使用 CLI 就必须约定好指令格式,而 shell 脚本就是一种用于 CLI 交互的指令格式。
因为这个比较特别的场景,shell 脚本有一些与其他编程语言不同的特点。一个很重要的特点,shell 脚本要比较简洁,容易输入。如果发送一条简单指令就要打几十个字符,那恐怕谁也无法接受。而为了达到可以接受的简洁程度,shell 脚本的语法,往往比其他编程语言的更加怪异。
因为这个比较特别的场景,shell 脚本有一些与其他编程语言不同的特点。一个很重要的特点,shell 脚本要比较简洁,容易输入。如果发送一条简单指令就要打几十个字符,那恐怕谁也无法接受。而为了达到可以接受的简洁程度,shell 脚本的语法,往往比其他编程语言的更加怪异。
有人可能会说,这搞混了两个事情。在 CLI 输入命令和写脚本文件然后执行命令是两回事,不需要使用同一种语言,而只是在 CLI 交互中,通常是没有必要写复杂逻辑的,也就是说 shell 脚本基本没有必要学习。
Expand All @@ -76,7 +76,7 @@ Python 是为通用的场景设计的,虽然也能处理 shell 脚本所做的

一个样例:

```
```zsh
# 前两行是输入内容,第三行是输出内容
% echo "Hello \
> World"
Expand All @@ -99,7 +99,7 @@ zsh 有 5 种变量:整数、浮点数(bash 不支持)、字符串、数

Zsh 的变量多数情况不需要提前声明或者指定类型,可以直接赋值和使用(但哈希表是一个例外)。

```
```zsh
# 等号两端不能有空格
% num1=123
% num2=123.456
Expand Down Expand Up @@ -132,7 +132,7 @@ def

#### 变量比较

```
```zsh
# 比较数值
% num=123
# (( )) 用于数值比较等操作,如果为真返回 0,否则返回 1
Expand Down Expand Up @@ -164,7 +164,7 @@ zsh 支持多种风格的语法,包括经典的 posix shell (bash 的语法

#### 条件语句

```
```zsh
# 格式
if [[ ]] {
} elif {
Expand All @@ -176,7 +176,7 @@ if [[ ]] {
`[[ ]]` 用于比较字符串、判断文件等,功能比较复杂多样,这里先使用最基础的用法。注意尽量不要用 `[[ ]]` 比较数值,因为不留神的话,数值会被转化成字符串来比较,没有任何错误提示,但结果可能不符合预期,导致不必要的麻烦。
```
```zsh
# 样例
if [[ "$str" == "name" || "$str" == "value" ]] {
echo "$str"
Expand All @@ -185,13 +185,13 @@ if [[ "$str" == "name" || "$str" == "value" ]] {
`(( ))` 用于比较数值,里边可以调用各种数值相关的函数,格式类似 c 语言,变量前的 `$` 可省略。
```
```zsh
# 格式
if (( )) {
}
```
```
```zsh
# 样例
if ((num > 3 && num + 3 < 10)) {
echo $num
Expand All @@ -200,37 +200,38 @@ if ((num > 3 && num + 3 < 10)) {
`{ }` 用于在当前 shell 运行命令并且判断运行结果。
```
```zsh
# 格式
if { } {
}
```
```
```zsh
# 样例
if {grep sd1 /etc/fstab} {
echo good
}
```
`( )` 用于在子 shell 运行命令并且判断运行结果,用法和 {} 类似,不再举例。
```
```zsh
# 格式
if ( ) {
}
```
这几种括号可以一起使用,这样可以同时判断字符串、数值、文件、命令结果等等。最好不要混合使用 `&&` `||`,会导致可读性变差和容易出错。
```
```zsh
# 格式
if [[ ]] && (( )) && { } {
}
```
#### 循环语句
```
```zsh
# 格式
while [[ ]] {
break/continue
Expand All @@ -239,7 +240,7 @@ while [[ ]] {
`if` 一样,这里的 `[[ ]]` 可以替换成其他几种括号,功能也是一样的,不再依次举例。`break` 用于结束循环,`continue` 用于直接进入下一次循环。所有的循环语句中都可以使用 `break``continue`,下边不再赘述。
```
```zsh
# 样例 死循环
while ((1)) {
echo good
Expand All @@ -248,28 +249,28 @@ while [[ ]] {
`until``while` 相反,不满足条件时运行,一旦满足则停止,其他的用法和 `while` 相同,不再举例。
```
```zsh
# 格式
until [[ ]] {
}
```
`for` 循环主要用于枚举,这里的括号是 `for` 的特有用法,不是在子 shell 执行。括号内是字符串(可放多个,空格隔开)、数组(可放多个)或者哈希表(可放多个,哈希表是枚举值而不是键)。`i` 是用于枚举内容的变量名,变量名随意。
```
```zsh
# 格式
for i ( ) {
}
```
```
```zsh
# 样例
for i (aa bb cc) {
echo $i
}
# 枚举当前目录的 txt 文件
for i (*.txt) {
for i (*.txt) {
echo $i
}
Expand All @@ -282,13 +283,13 @@ for i ($array) {
经典的 c 风格 `for` 循环。
```
```zsh
# 格式
for (( ; ; )) {
}
```
```
```zsh
# 样例
for ((i=0; i < 10; i++)) {
echo $i
Expand All @@ -297,7 +298,7 @@ for ((i=0; i < 10; i++)) {
这个样例只是举例,实际上多数情况不需要使用这种 `for` 循环,可以这样。
```
```zsh
# 样例,{1..10} 可以生成一个 1 到 10 的数组
for i ({1..10}) {
echo $i
Expand All @@ -306,13 +307,13 @@ for i ({1..10}) {
`repeat` 语句用于循环固定次数,`n` 是一个整数或者内容为整数的变量。
```
```zsh
# 格式
repeat n {
}
```
```
```zsh
# 样例
repeat 5 {
echo good
Expand All @@ -323,7 +324,7 @@ repeat 5 {
分支逻辑用 `if` 也可以实现,但 `case` 更适合这种场景,并且功能更强大。
```
```zsh
# 格式 + 样例
case $i {
(a)
Expand Down Expand Up @@ -360,13 +361,13 @@ case $i {
`select` 语句是用于根据用户的选择决定分支的语句,语法和 `for` 语句差不多,如果不 `break`,会循环让用户选择。
```
```zsh
# 格式
select i ( ) {
}
```
```
```zsh
# 样例
select i (aa bb cc) {
echo $i
Expand All @@ -375,7 +376,7 @@ select i (aa bb cc) {
输出是这样的。
```
```zsh
1) aa 2) bb 3) cc
?#
```
Expand All @@ -384,7 +385,7 @@ select i (aa bb cc) {
#### 异常处理语句
```
```zsh
# 格式
{
语句 1
Expand All @@ -399,7 +400,7 @@ select i (aa bb cc) {
`if` 语句的简化版,在只有一个分支的情况下更简洁,功能和 `if` 语句类似,不赘述。
```
```zsh
格式:
[[ ]] || {
}
Expand All @@ -410,13 +411,13 @@ select i (aa bb cc) {
最好不要连续混合使用 `&&` `||`,比如。
```
```zsh
aa && bb || cc && dd
```
容易导致逻辑错误或者误解,可以用 `{ }` 把语句包含起来。
```
```zsh
aa && { bb || { cc && dd } }
```
Expand Down
Loading

0 comments on commit aac8b3c

Please sign in to comment.