Gore Liu 7 years ago
parent
commit
b81ef1cd26

File diff suppressed because it is too large
+ 426 - 0
Zsh-开发指南(第一篇-变量和语句).md


+ 118 - 0
Zsh-开发指南(第七篇-数值计算).md

@@ -0,0 +1,119 @@
+### 导读
+
+数值计算并非 zsh 的强项,但应付一些简单的场景还是没问题的。并且 zsh 提供一个数值计算库,里边有一些比较常用的数学函数。
+
+### 整数和浮点数类型
+
+Zsh 中通常不用指定变量类型,但也可以指定。对数值计算来说,区分整数和浮点数是很重要的,不指定变量类型会带来不方便。
+
+```
+# 整数
+% integer i=123
+# (t) 用于输出变量类型
+% echo ${(t)i}
+integer
+
+# 浮点数
+% float f=123.456
+% echo ${(t)f}
+float
+
+# 注意一旦指定的变量类型,类型就不会变了,除非再重新指定其他类型,或者用 unset 删除掉 
+# 如果把浮点数赋值给整数变量,会取整
+% i=12.34
+% echo $i
+12
+% a=-12.34
+% echo $a
+-12
+
+# 整数是 64 位的带符号整数(在 32 位系统下也是)
+% echo $((-2 ** 63)) $((2 ** 63 - 1))
+-9223372036854775808 9223372036854775807
+
+# 浮点数是 64 位带符号浮点数(在 32 位系统下也是)
+% echo $((-1.79e-308)) $((1.79e308))
+-1.79e-308 1.79e+308
+```
+
+### 运算符
+
+数值计算主要是在 (( )) 或者 $(( )) 中进行的,在 $[ ] 或者 $var[ ](可用于数组索引的计算)中也能进行一部分,这里统一使用小括号。
+
+```
+% integer i=123
+% float f=123.456
+
+# $(( )) 会计算后返回数值
+% echo $((i*f))
+15185.088
+
+# (( )) 用于判断数值比较的结果
+% ((i < f && i + 1 > f)) && echo good
+
+# 在 (( )) 中也可以给变量赋值
+# (( )) 中的语法类似 c 语言,变量名前不需要 $,等号两边可以有空格
+% float result
+% ((result = i / f))
+% echo $result
+9.963063764e-01
+```
+
+运算符列表:
+
+运算符 | 功能 | 样例
+-- | -- | --
+\+ - * / | 四则运算 | 1 + 2 * 3 / 4
+** | 乘方 | 3 ** 3.5
+% | 取余 | 5 % 3
+& \| ^ | 按位与、按位或、按位异或 | 11 & 13
+~ | 按位取反 | ~15
+<< >> | 按位左移、按位右移 | 1 << 3
+< <= > >= | 大小比较 | 2 <= 4
+== != | 相等比较 | 2 != 4
+&& \|\| | 逻辑与、逻辑或 | 2 <= 4 && 1 != 3 \|\| 5 > 0
+! | 逻辑非 | ! 1 > 2
+^^ | 逻辑异或(两个中只有一个为真) |  1 > 0 ^^ 1 < 0
+= | 赋值 | i = 10
++= -= *= /= %= **= &= ^= \|= <<= >>= &&= ^^= \|\|= | 复合赋值| i += 10
+( ) | 调整优先级 | (1 + 2 ) * 3
+? : | 三元运算符 |  1 > 2 ? 100 : 200
+, | 逗号运算符(只返回后者) | 40, 20 == 20
+
+运算符的优先级和其他编程语言的差不多,不列出了,如果不确定可以加小括号。这部分内容和 c、java、javascript 等语言基本一致。
+
+### 数学函数
+
+Zsh 包含了一个数学模块,如果需要使用数学函数,需要先加载 `zsh/mathfunc` 模块。
+
+```
+% zmodload -i zsh/mathfunc
+
+% echo $((sin(0) + ceil(14.4)))
+15.0
+```
+
+函数列表:
+
+函数名 | 功能
+-- | --
+abs | 求绝对值
+ceil | 向上取整
+floor | 向下取整
+int | 截断取整
+float | 转换成浮点数
+sqrt | 开平方
+cbrt | 开立方
+log | 自然对数
+log10 | 常用对数
+rand48 | 随机数
+
+更多函数:
+
+acos, acosh, asin, asinh, atan, atanh,  cos, cosh, erf, erfc, exp, expm1, fabs, gamma, j0, j1, lgamma, log1p, logb, sin, sinh, tan, tanh, y0, y1, ilogb, signgam, copysign, fmod, hypot, nextafter, jn, yn, ldexp, scalb
+
+
+### 参考
+
+http://www.bash2zsh.com/zsh_refcard/refcard.pdf

+ 459 - 0
Zsh-开发指南(第三篇-字符串处理之转义字符和格式化输出).md

@@ -0,0 +1,459 @@
+### 导读
+
+上一篇讲了 zsh 的常用字符串操作,这篇开始讲更为琐碎的转义字符和格式化输出相关内容。包括转义字符、引号、print、printf 的使用等等。其中很多内容没有必要记忆,作为手册参考即可。
+
+### 转义字符
+
+转义字符是很多编程语言中都有的概念,它主要解决某些字符因为没有对应键盘按键无法直接输出、字符本身有特殊含义(比如 `\`、`"`)或者显示不直观(比如难以区别多个空格和一个 tab)等问题。
+
+最常用的转义字符是 `\n`(换行)、`\r`(回车)、`\t`(tab)。
+
+直接用 `echo`、`print` 或者 `printf` 内置命令都可以正常输出转义字符,但包括转义字符的字符串需要用引号(单双引号都可以)扩起来。
+
+```
+% echo 'Hello\n\tWorld'
+Hello
+        World
+```
+
+常用转义字符对照表,不常用的可以去查 ASCII 码表,然后使用 `\xnn`(如 `\x14`)。
+
+| 转义字符 | 含义      | ASCII 码值(十六进制)|
+| -------- |---------- | --------------------- |
+| \n       | 换行      | 0a                    |
+| \r       | 回车      | 0d                    |
+| \t       | tab       | 09                    |
+| \\\\     | \         | 5c                    |
+| \\`      | `         | 60                    |
+| \\xnn    | 取决于 nn | nn                    |
+
+可以用 `hexdump` 命令查看字符的 ASCII 码值。
+
+```
+% echo ab= | hexdump -C
+00000000  61 62 3d 0a                                       |ab=.|
+00000004
+```
+
+还有一些字符是可选转义(通常有特殊含义的字符都是如此)的,比如空格、`"`、`'`、`*`、`~`、`$`、`&`、`(`、`)`、`[`、`]`、`{`、`}`、`;`、`?` 等等,即如果在引号里边则无需转义(即使转义也不出错,转义方法都说前边加一个 `\`),但如果在引号外边则需要转义。谨慎起见,包含半角符号的字符串全部用引号包含即可,可以避免不必要的麻烦。
+
+可以这样检查一个字符在空格外是否需要转义,输出的字符中前边带 `\` 的都是需要的。
+
+```
+% str='~!@#$%^&*()_+-={}|[]:;<>?,./"'
+# -r 选项代表忽略字符串中的转义符合
+# ${(q)str} 功能是为字符串中的特殊符号添加转义符号
+% print -r ${(q)str}
+\~\!@\#\$%\^\&\*\(\)_+-=\{\}\|\[\]:\;\<\>\?,./\"
+```
+
+### 单引号
+
+单引号的左右主要是为了避免字符串里的特殊字符起作用。在单引号中,只有一个字符需要转义,转义符号 `\` 。所以如果字符串里包含特殊符号时,最好使用单引号包含起来,避免不必要的麻烦。但是单引号里不能再有单引号,即使加转义符号也不行,如果字符串需要包含单引号,可以使用这几种方法。
+
+```
+# 用双引号包含
+% echo "a'b"
+a'b
+
+# 用转义符号
+% echo a\'b
+a'b
+
+# 同时使用单引号和转义符号,用于包含单引号和其他特殊符号的场景
+% echo 'a"\'\''b*?'
+a"\'b*?
+```
+
+### 双引号
+
+双引号的作用类似单引号,但没有单引号那么严格,有些特殊字符在双引号里可以继续起作用。
+
+```
+# 以使用变量
+% str=abc
+% echo "$str"
+abc
+
+# 可以使用 $( ) 运行命令
+% echo "$(ls)"
+git
+tmp
+
+# 可以使用 ` ` 运行命令,不建议在脚本里使用 ` `
+% echo "`date`"
+Mon Aug 28 09:49:11 CST 2017
+
+# 可以使用 $(( )) 计算数值
+% echo "$(( 1 + 2 ))"
+3
+
+# 可以使用 $[ ] 计算数值
+% echo "$[1 + 2]"
+3
+```
+
+简单说,$ 加各种东西的用法在双引号里都是可以正常使用的,而其他特殊符号(比如 `*`、`?`、`>`)的功能通常不可用。
+
+### 反引号
+
+反引号是用来运行命令的,它会返回命令结果,以便保存到变量等等。
+
+```
+% str=`ls`
+% echo $str
+git
+tmp
+
+# 完全可以用 $( ) 取代
+% str=$(ls)
+% echo $str
+git
+tmp
+```
+
+反引号的功能和 `$( )` 功能基本一样,但 `$( )` 可以嵌套,而反引号不可以,而且反引号看起来更费事,某些字体中的反引号和单引号差别不大。所以在脚本里不建议使用反引号。
+
+### print 命令用法
+
+print 是类似 echo 的内部命令(echo 命令很简单,不作介绍),但功能比 echo 强大很多。完全可以使用 print 代替 echo。
+
+不加参数的 print 和 echo 的功能基本一样,但如果字符串里包含转义字符,某些情况可能不一致。如果需要输出转义字符,尽量统一使用 print,避免不一致导致的麻烦。
+
+```
+% print 'Line\tone\n\Line\ttwo'
+Line    one
+Line    two
+
+# echo 的输出和 print 不一致
+% echo 'Line\tone\n\Line\ttwo'
+Line    one
+\Line   two
+```
+
+print 有很多参数,在 zsh 里输入 print - 然后按 tab 即可查看选项帮助(如果没有效果,需要配置 .zshrc 里的补全选项,网上有很多现成的配置)。
+
+```
+# - 后直接按 tab,C 是补全上去的
+% print -C
+ -- option --
+-C  -- print arguments in specified number of columns
+-D  -- substitute any arguments which are named directories using ~ notation
+-N  -- print arguments separated and terminated by nulls
+...
+```
+
+### print 命令选项功能介绍
+
+这里以常用程度的顺序依次介绍所有的选项,另外文末有“print 选项列表”方便查询。
+
+-l 用于分行输出字符串。
+
+```
+# 每个字符串一行,字符串是用空格隔开的
+% print -l aa bb
+aa
+bb
+
+# 也可以接数组,数组相关的内容之后会讲到
+# 命令后的多个字符串都可以用数组取代,效果是相同的
+% array=(aa bb)
+% print -l $array
+aa
+bb
+```
+
+-n 用于不在输出内容的末尾自动添加换行符(echo 命令也有这个用法)。
+
+```
+% print abc
+abc
+# 这个 % 高亮显示,代表这一行末尾没有换行符
+% print -n abc
+abc%
+```
+
+-m 用于只输出匹配到的字符串。
+
+```
+% print -m "aa*" aabb abc aac
+aabb aac
+```
+
+-o/-O/-i 用于对字符串排序。
+
+```
+# print -o 对字符串升序排列
+% print -o a d c 1 b g 3 s
+1 3 a b c d g s
+
+# print -O 对字符串降序排列
+% print -O a d c 1 b g 3 s
+s g d c b a 3 1
+
+# 加 -i 参数后,对大小写不敏感
+% print -oi A B C a c A B C
+A a A B B C c C
+
+# 不加 -i 的话小写排在大写的前面
+% print -o A B C a c A B C
+a A A B B c C C
+```
+
+-r 用于不对字符串进行转义。print 默认是会对转义字符进行转义的,加 -r 后会原样输出。
+
+```
+% print -r '\n'
+\n
+```
+
+-c 用于将字符串按列输出。如果对自动决定的列数不满意,可以用 -C 指定列数。
+
+```
+% print -c a bbbbb ccc ddddd ee ffffff gg hhhhhh ii jj kk
+a       ccc     ee      gg      ii      kk
+bbbbb   ddddd   ffffff  hhhhhh  jj
+```
+
+-C 用于按指定列数输出字符串。
+
+```
+# 从上到下
+% print -C 3 a bb ccc dddd ee f
+a     ccc   ee
+bb    dddd  f
+
+% print -C 3 a bb ccc dddd ee f g
+a     dddd  g
+bb    ee
+ccc   f
+
+# 加 -a 后,改成从左向右
+% print -a -C 3 a bb ccc dddd ee f g
+a     bb    ccc
+dddd  ee    f
+g
+```
+
+-D 用于将符合条件的路径名转化成带 ~ 的格式,~ 是家目录。
+
+```
+% print -D /home/goreliu/git
+~/git
+
+# mine 是这样定义的 hash -d mine='/mnt/c/mine'
+% print -D /mnt/c/mine
+~mine
+```
+
+-N 用于将输出的字符串以 `\x00`(null)分隔,而不是空格。这样可能方便处理包含空格的字符串,xargs 等命令也可以接受以 `\x00` 分隔的字符串。
+
+```
+% print -N aa bb cc
+aabbcc%
+
+% print -N aa bb cc | hexdump -C
+00000000  61 61 00 62 62 00 63 63  00                       |aa.bb.cc.|
+00000009
+```
+
+-x 用于将行首的 tab 替换成空格。-x 是将行首的 tab 展开成空格,- x 后的参数是一个 tab 对应的空格数。
+
+```
+% print -x 2 '\t\tabc' | hexdump -C
+00000000  20 20 20 20 61 62 63 0a                           |    abc.|
+00000008
+
+% print -x 4 '\t\tabc' | hexdump -C
+00000000  20 20 20 20 20 20 20 20  61 62 63 0a              |        abc.|
+0000000c
+```
+
+-X 用于将所有的 tab 替换成空格。注意不是简单地替换成空格。比如每行有一个 tab,-X 8,那么如果 tab 前(到行首或者上一个 tab)有 5 个字符,就补全 3 个空格,凑够 8,这么做是为了对齐每一列的。但如果前边有 8 个或者 8 个以上字符,那么依然是一个 tab 替换成 8 个字符,因为 tab 不能凭空消失,一定要转成至少一个空格才行。如果没理解就自己多试试找规律吧。
+
+```
+% print -X 2 'ab\t\tabc' | hexdump -C
+00000000  61 62 20 20 20 20 61 62  63 0a                    |ab    abc.|
+0000000a
+
+% print -X 4 'ab\t\tabc' | hexdump -C
+00000000  61 62 20 20 20 20 20 20  61 62 63 0a              |ab      abc.|
+0000000c
+```
+
+-u 用于指定 fd 输出。print 默认输出到 fd 1,即 stdout,可以指定成其他 fd(2 是 stderr,其他的可以运行 `ls -l /proc/$$/fd` 查看。
+
+```
+% print -u 2 good
+good
+
+# 和重定向输出效果一样
+% print good >&2
+```
+
+-v 用于把输出内容保存到变量。
+
+```
+# 和 str="$(print aa bb cc)" 效果一样
+% print -v str aa bb cc
+% echo $str
+aa bb cc
+```
+
+-s/-S 用于把字符串保存到历史记录。
+
+```
+% print -s ls -a
+% history | tail -n 1
+ 2222  ls -a
+
+# -S 也类似,但需要用引号把命令引起来
+% print -S "ls -a"
+% history | tail -n 1
+ 2339  ls -a
+```
+
+-z 用于把字符串输出到命令行编辑区。
+
+```
+# _是光标位置
+% print -z aa bb cc
+% aa bb cc_
+```
+
+-f 用于按指定格式化字符串输出,同 printf,用法见“printf 命令用法”。
+
+-P 用于输出带颜色和特殊样式的字符串,见“输出带颜色和特殊样式的字符串”。
+
+-b 用于辨认出 bindkey 中的转义字符串,bindkey 是 Zle 的快捷键配置内容,写脚本用不到,不作介绍。
+
+-R 用于模拟 echo 命令,只支持 -n 和 -e 选项,通常用不到。
+
+### printf 命令用法
+
+printf 命令很像 c 语言的 printf 函数,用于输出格式化后的字符串。
+
+```
+# 输出末尾的 % 代表该行末尾没有换行符
+# printf 不会在输出末尾添加换行符
+# 为了避免误解,之后的例子省略该符号
+% printf ":%d %f:" 12 34.56
+:12 34.560000:%
+```
+
+printf 的第一个参数是格式化字符串,在 zsh 里输入 printf % 后按 tab,可以看到所有支持的用法。下面只举几个比较常用的例子。
+
+```
+# 整数 浮点数 字符串
+% printf "%d %f %s" 12 12.34 abcd
+12 12.340000 abcd%
+
+# 取小数点后几位
+% printf "%.1f" 12.34
+12.3
+
+# 科学计数法输出浮点数
+% printf "%e" 12.34
+1.234000e+01
+
+# 将十进制数字转成十六进制输出
+% printf "%x" 12
+c
+
+# 补齐空格或者0
+% printf "%5d\n%05d" 12 12
+   12
+00012
+```
+
+我把完整的格式贴在这里,方便搜索。
+
+```
+ -- print format specifier --
+      -- leave one space in front of positive number from signed conversion
+-     -- left adjust result
+.     -- precision
+'     -- thousand separators
+*     -- field width in next argument
+#     -- alternate form
+%     -- a percent sign
++     -- always place sign before a number from signed conversion
+0     -- zero pad to length
+b     -- as %s but interpret escape sequences in argument
+c     -- print the first character of the argument
+E  e  -- double number in scientific notation
+f     -- double number
+G  g  -- double number as %f or %e depending on size
+i  d  -- signed decimal number or with leading " numeric value of following character
+n     -- store number of printed bytes in parameter specified by argument
+o     -- unsigned octal number
+q     -- as %s but shell quote result
+s     -- print the argument as a string
+u     -- unsigned decimal number
+X  x  -- unsigned hexadecimal number, letters capitalized as x
+```
+
+### 输出带颜色和特殊样式的字符串
+
+用 zsh 的 print -P 可以方便地输出带颜色和特殊样式的字符串,不用再和 `\033[41;36;1m` 之类莫名其妙的字符串打交道了。
+
+```
+# %B 加粗 %b 取消加粗
+# %F{red} 前景色 %f 取消前景色
+# %K{red} 背景色 %k 取消背景色
+# %U 下滑线 %u 取消下滑线
+# %S 反色 %s 取消反色
+#
+# black or 0  red     or 1
+# green or 2  yellow  or 3
+# blue  or 4  magenta or 5
+# cyan  or 6  white   or 7
+
+# 显示加粗的红色 abc
+% print -P '%B%F{red}abc'
+abc
+
+
+# 没覆盖到的功能可以用原始的转义符号,可读性比较差
+# 4[0-7] 背景色
+# 3[0-7] 前景色
+# 0m 正常 1m 加粗 2m 变灰 3m 斜体 4m 下滑钱 5m 闪烁 6m 快速闪烁 7m 反色
+
+# 显示闪烁的红底绿字 abc
+% print "\033[41;32;5mabc\033[0m"
+abc
+```
+
+### print 选项列表
+
+为了方便查询,我把 print 的选项列表放在这里。
+
+| 选项  | 功能                                | 参数           |
+| ----- | ----------------------------------- | ---------------|
+| -C    | 按列输出                            | 列数           |
+| -D    | 替换路径成带 ~ 的版本               | 无             |
+| -N    | 使用 \x00 作为字符串的间隔          | 无             |
+| -O    | 降序排列                            | 无             |
+| -P    | 输出颜色和特殊样式                  | 无             |
+| -R    | 模拟 echo 命令                      | 无             |
+| -S    | 放命令放入历史命令文件(要加引号)  | 无             |
+| -X    | 替换所有 tab 为空格                 | tab 对应空格数 |
+| -a    | 和 -c/-C 一起使用时,改为从左到右   | 无             |
+| -b    | 识别出 bindkey 转义字符串           | 无             |
+| -c    | 按列输出(自动决定列数)            | 无             |
+| -f    | 同 printf                           | 无             |
+| -i    | 和 -o/-O 一起用时,大小写不敏感排序 | 无             |
+| -l    | 使用换行符作为字符串分隔符          | 无             |
+| -m    | 只输出匹配的字符串                  | 匹配模式字符串 |
+| -n    | 不自动添加最后的换行符              | 无             |
+| -o    | 升序排列                            | 无             |
+| -r    | 不处理转义字符                      | 无             |
+| -s    | 放命令放入历史命令文件(不加引号)  | 无             |
+| -u    | 指定 fd 输出                        | fd 号          |
+| -v    | 把内容保存到变量                    | 变量名         |
+| -x    | 替换行首的 tab 为空格               | tab 对应空格数 |
+| -z    | 把内容放置到命令行编辑区            | 无             |
+
+### 参考
+
+http://zsh.sourceforge.net/Guide/zshguide05.html

File diff suppressed because it is too large
+ 241 - 0
Zsh-开发指南(第九篇-函数和脚本).md


File diff suppressed because it is too large
+ 364 - 0
Zsh-开发指南(第二篇-字符串处理之常用操作).md


+ 450 - 0
Zsh-开发指南(第五篇-数组).md

@@ -0,0 +1,450 @@
+### 导读
+
+了解完结构比较简单的字符串后,我们来看更复杂一些的数组。其实字符串在 zsh 中也可以当字符数组操作,但很少有需要把字符串当数组来处理的场景。本篇中主要讲的是字符串数组,复杂度要比单个字符串高一些。
+
+在实际的脚本编写中,较少需要处理单个的字符串。往往需要处理从各个地方过来的大量文本,不可避免会用到数组。用好数组,会让文本处理工作事半功倍。
+
+本篇只涉及数组的基础用法。
+
+### 数组定义
+
+数组可以直接赋值使用,不需要提前声明。等号和小括号之间不能有空格,小括号中的元素以空格隔开。
+
+```
+% array=(a bc ccc dddd)
+# 用 $array 即可访问数组全部元素,输出时元素以空格分隔
+% echo $array
+a bc ccc dddd
+
+# 使用 print -l 可以每行输出一个元素
+% print -l $array
+a
+bc
+ccc
+dddd
+
+# 输出数组中的元素个数,用法和取字符串长度一样
+% echo $#array
+4
+
+# 包含带空格的字符串
+% array=(a "bc ccc" dddd)
+% print -l $array
+a
+bc ccc
+dddd
+
+# 可以换行赋值,但如果行中间有空格,依然需要加引号
+% array=(
+> a
+> bb
+> "c c c"
+> dddd
+> )
+```
+
+### 元素读写
+
+```
+% array=(a bc ccc dddd)
+
+# 用法和取字符串的第几个字符一样,从 1 开始算
+% echo $array[3]
+ccc
+# -1 依然是最后一个元素,-2 是倒数第二个,以此类推
+% echo $array[-1]
+dddd
+
+% array[3]=CCC
+
+# 如果赋值的内容是一个空的小括号,则删除该元素
+% array[2]=()
+
+% print -l $array
+a
+CCC
+dddd
+
+# 用 += 为数组添加一个新元素
+% array+=eeeee
+% print -l $array
+a
+CCC
+dddd
+eeeee
+
+# 用 unset 可以删除整个数组
+% unset array
+
+# array 变量变成未定义状态
+% echo $+array
+0
+```
+
+### 数组拼接
+
+```
+% array1=(a b c d)
+% array2=(1 2 3 4)
+
+# 用 += 拼接数组
+% array1+=(e f g)
+% echo $array1
+a b c d e f g
+
+# 拼接另一个数组,小括号不可以省略,否则 array1 会被转成一个字符串
+% array2+=($array1)
+% echo $#array2
+11
+
+# 去掉引号后,array1 被转成了一个字符串
+% array2+=$array1
+% echo $#array2
+12
+% echo $array2[12]
+a b c d e f g
+
+
+# 字符串可以直接拼接数组而转化成数组
+% str=abcd
+% str+=(1234)
+
+% echo $#str
+2
+```
+
+### 数组遍历
+
+```
+% array1=(a bb ccc dddd)
+% array2=(1 2 3)
+
+# 用 for 可以直接遍历数组,小括号不可省略
+% for i ($array1) {
+> echo $i
+> }
+a
+bb
+ccc
+dddd
+
+# 小括号里可以放多个数组,依次遍历
+% for i ($array1 $array2) {
+> echo $i
+> }
+a
+bb
+ccc
+dddd
+1
+2
+3
+```
+
+### 数组切片
+
+数组切片和字符串切片操作方法完全相同。
+
+```
+% array=(a bb ccc dddd)
+
+% echo $array[2,3]
+bb ccc
+
+# 依然可以多对多地替换元素
+% array[3,-1]=(1 2 3 4)
+% echo $array
+a bb 1 2 3 4
+
+# 也可以使用另一种语法,不建议使用
+% echo ${array:0:3}
+a bb 1
+```
+
+### 元素查找
+
+数组的元素查找方法,和字符串的子字符串查找语法一样。
+
+```
+% array=(a bb ccc dddd ccc)
+
+# 用小 i 输出从左到右第一次匹配到的元素位置
+% echo $array[(i)ccc]
+3
+
+# 如果找不到,返回数组大小 + 1
+% echo $array[(i)xxx]
+6
+
+# 用大 I 输出从右到左第一次匹配到的元素位置
+% echo $array[(I)ccc]
+5
+
+# 如果找不到,返回 0 
+% echo $array[(I)xxx]
+0
+
+# 可以用大 I 判断是否存在元素
+% (( $array[(I)dddd] )) && echo good
+good
+
+% (( $array[(I)xxx] )) && echo good
+
+
+% array=(aaa bbb aab bbc)
+# n:2: 从指定的位置开始查找
+% echo ${array[(in:2:)aa*]}
+3
+```
+
+### 元素排序
+
+```
+% array=(aa CCC b DD e 000 AA 3 aa 22)
+
+# 升序排列,从小到大
+% echo ${(o)array}
+000 22 3 aa aa AA b CCC DD e
+
+# 降序排列,从大到小
+% echo ${(O)array}
+e DD CCC b AA aa aa 3 22 000
+
+# 加 i 的话大小写不敏感
+% echo ${(oi)array}
+000 22 3 aa AA aa b CCC DD e
+
+
+% array=(cc aaa b 12 115 90)
+# 加 n 的话按数字大小顺序排
+% echo ${(on)array}
+12 90 115 aaa b cc
+
+# Oa 用于反转数组元素的排列顺序
+% echo ${(Oa)array}
+90 115 12 b aaa cc
+```
+
+### 去除重复元素
+
+```
+% array=(ddd a bb a ccc bb ddd)
+
+% echo ${(u)array}
+ddd a bb ccc
+```
+
+### 使用连续字符或者数值构造数组
+
+```
+# 大括号中的逗号分隔的字符串会被展开
+% array=(aa{bb,cc,11}) && echo $array
+aabb aacc aa11
+
+# .. 会将前后的数组连续展开
+% array=(aa{1..3}) && echo $array
+aa1 aa2 aa3
+
+# 第二个 .. 后的数字是展开的间隔
+% array=(aa{15..19..2}) && echo $array
+aa15 aa17 aa19
+
+# 可以从大到小展开
+% array=(aa{19..15..2}) && echo $array
+aa19 aa17 aa15
+
+# 可以添加一个或多个前导 0
+% array=(aa{01..03}) && echo $array
+aa01 aa02 aa03
+
+# 单个字母也可以像数值那样展开,多个字母不行
+% array=(aa{a..c}) && echo $array
+aaa aab aac
+
+# 字母是按 ASCII 码的顺序展开的
+% array=(aa{Y..c}) && echo $array
+aaY aaZ aa[ aa\ aa] aa^ aa_ aa` aaa aab aac
+
+
+# 这些用法都可以用在 for 循环里边
+% for i (aa{a..c}) {
+> echo $i
+> }
+aaa
+aab
+aac
+```
+
+### 从字符串构造数组
+
+```
+% str="a bb ccc dddd"
+
+# ${=str} 可以将 str 内容按空格切分成数组
+% array=(${=str})
+% print -l $array
+a
+bb
+ccc
+dddd
+
+% str="a\nbb\nccc\ndddd"
+# 如果需要修改分隔符,可以设置 SH_WORD_SPLIT 变量
+% SH_WORD_SPLIT="\n"
+% array1=(${=str})
+
+# 或者用 (s:x:) 方法,如果分隔符是冒号就用 s.:.
+% array2=(${(s:'\n':)str})
+% print -l $array1 $array2
+a
+bb
+ccc
+dddd
+a
+bb
+ccc
+dddd
+```
+
+### 从文件构造数组
+
+test.txt 内容。
+
+```
+a
+bb
+ccc
+dddd
+```
+
+每行一个元素。
+
+```
+# f 的功能是将字符串以换行符分隔成数组
+# 双引号不可省略,不然会变成一个字符串,引号也可以加在 ${ } 上
+% array=(${(f)"$(<test.txt)"})
+% print -l $array
+a
+bb
+ccc
+dddd
+
+# 不加引号的效果
+% array=(${(f)$(<test.txt)})
+% print -l $array
+a bb ccc dddd
+
+
+# 从文件构造数组,并将每行按分隔符 : 分隔后输出所有列
+for i (${(f)"$(<test.txt)"}) {
+    array=(${(s.:.)i})
+    echo $array[1,-1]
+}
+```
+
+### 从文件列表构造数组
+
+```
+# 这里的 * 即上一篇讲的通配符,所有的用法都可以在这里使用。
+% array=(/usr/bin/vim*)
+% print -l $array
+/usr/bin/vim
+/usr/bin/vimdiff
+/usr/bin/vimtutor
+
+# 要比 ls /usr/bin/[a-b]?? | wc -l 快很多
+% array=(/usr/bin/[a-b]??) && print $#array
+3
+```
+
+### 数组交集差集
+
+```
+% array1=(1 2 3)
+% array2=(1 2 4)
+
+# 两个数组的交集,只输出两个数组都有的元素
+% echo ${array1:*array2}
+1 2
+
+# 两个数组的差集,只输出 array1 中有,而 array2 中没有的元素
+% echo ${array1:|array2}
+3
+
+# 如果有重复元素,不会去重
+% array1=(1 1 2 3 3)
+% array2=(4 4 1 1 2 2)
+% echo ${array1:*array2}
+1 1 2
+```
+
+### 数组交叉合并
+
+```
+% array1=(a b c d)
+% array2=(1 2 3)
+
+# 从 array1 取一个,再从 array2 取一个,以此类推,一个数组取完了就结束
+% echo ${array1:^array2}
+a 1 b 2 c 3
+
+# 如果用 :^^,只有一个数组取完了的话,继续从头取,直到第二个数组也取完了
+% echo ${array1:^^array2}
+a 1 b 2 c 3 d 1
+```
+
+### 对数组中的字符串进行统一的处理
+
+一些处理字符串的方法(主要是各种形式的截取、替换、转换等等),也可以用在数组上,效果是对数组中所有元素统一处理。
+
+```
+% array=(/a/b.htm /a/c /a/b/c.txt)
+
+# :t 是取字符串中的文件名,可以用在数组上,取所有元素的文件名
+% print -l ${array:t}
+b.htm
+c
+c.txt
+
+# :e 是取扩展名,如果没有没有扩展名,结果数组中不会添加空字符串
+% print -l ${array:e}
+htm
+txt
+
+# 字符串替换等操作也可以对数组使用,替换所有字符串
+% print -l ${array/a/j}
+/j/b.txt
+/j/c
+/j/b/c.txt
+```
+
+:# 也可以在数组上用,但更实用一些。
+
+```
+% array=(aaa bbb ccc)
+
+# :# 是排除匹配到的元素,类似 grep -v
+% print ${array:#a*}
+bbb ccc
+
+# 前边加 (M),是反转后边的效果,即只输出匹配到的元素,类似 grep
+% print ${(M)array:#a*}
+aaa
+
+# 多个操作可以同时进行,(U) 是把字符串转成大写字母
+% print ${(UM)array:#a*}
+AAA
+```
+
+### 总结
+
+本篇讲的是数组的基础用法,还有很多复杂的操作方法,以后会提到。
+
+### 参考
+
+http://zshwiki.org/home/scripting/array
+http://www.bash2zsh.com/zsh_refcard/refcard.pdf
+
+### 更新历史
+
+20170830:增加“使用连续字符或者数值构造数组”。

+ 233 - 0
Zsh-开发指南(第八篇-变量修饰符).md

@@ -0,0 +1,233 @@
+### 导读
+
+我们已经了解了字符串、数组、哈希表、整数、浮点数的基本用法,但应付某些复杂的场景依然力不从心。
+
+变量修饰符是 zsh 中有一个很独特的概念,对变量进行操作,功能上和函数类似,但用起来更方便,在一行代码里实现复杂功能主要靠它了。而代价是可读性更差,怎么用就要自己权衡了。它也是 zsh 最有特色的部分之一。变量修饰符主要应用于数组和哈希表,但也有一小部分可以应用于字符串(整数和浮点数也会被当成字符串处理)。
+
+### 变量修饰符的格式
+
+其实前边的文章中,变量修饰符已经出现过,但当时没有详细说明。
+
+比如在大小写转换的例子中。
+
+```
+% str="ABCDE abcde"
+
+# 转成大写,(U) 和 :u 两种用法效果一样
+% echo ${(U)str} --- ${str:u}
+ABCDE ABCDE --- ABCDE ABCDE
+
+# 转成小写,(L) 和 :l 两种用法效果一样
+% echo ${(L)str} --- ${str:l}
+abcde abcde --- abcde abcde
+```
+
+这里的 `(U)`、`:l` 等等都是变量修饰符。变量修饰符主要有两种格式。
+
+```
+${(x)var}
+${var:x}
+```
+
+其中 var 是变量名,x 是 一个或多个字母,不同字母的功能不同。第二行的冒号也可能是其他符号。${var} 和 $var 基本相同,大括号用于避免变量名中的字符和后边的字符粘连,通常情况是不需要加大括号的。但如果使用变量修饰符,大括号就必不可少(其实第二种格式中,大括号可以省略,但考虑可读性和错误提示等因素,还是加上比较好)。
+
+变量修饰符可以嵌套使用。因为加了修饰符的变量依然是变量,可以和正常的变量一样处理。
+
+```
+% str=abc
+% echo ${(U)str}
+ABC
+% echo ${(C)${(U)str}}
+Abc
+
+% echo ${${a:u}:l}
+abc
+
+# 可以两种风格嵌套在一起
+% echo ${(C)${a:u}}
+Abc
+```
+
+这里要注意 $ 之后全程不能有空格,否则会有语法错误。也就是说不能通过加空格来避免因为字符挤在一起造成的可读性变差。但熟悉了格式后,就可以比较容易识别出代码的功能。比较复杂的逻辑可以换行继续写,而没必要一定嵌套使用。
+
+知道了变量修饰符的用法后,重要的就是都有哪些可以使用的变量修饰符了。
+
+### 变量默认值
+
+和变量默认值(读取变量时如果变量为空或者不存在,使用的默认值)相关的操作,变量可以是任何类型的。
+
+```
+% var=123
+
+# 如果变量有值,就输出变量值
+% echo ${var:-abc}
+123
+
+# 如果变量没有值(变量不存在,为空字符串、空数组、空哈希表等),输出 abc
+% echo ${varr:-abc}
+abc
+
+
+% var=""
+# 和 :- 类似,但只有变量不存在时才替换成默认值
+% echo ${var-abc}
+% echo ${varr-abc}
+abc
+
+
+% var=""
+# 和 :- 类似,但如果变量没有值,则赋值为 abc
+% echo ${var:=abc}
+abc
+% echo $var
+abc
+
+
+% var=abc
+# 不管 var 有没有值,都赋值为 123
+% echo ${var::=123}
+123
+% echo $var
+123
+
+
+% var=""
+# 如果 var 没有值,直接报错
+% echo ${var:?error}
+zsh: var: error
+
+
+% var=abc
+# 如果 var 有值,输出 123
+% echo ${var:+123}
+% echo ${varr:+123}
+
+```
+
+### 数组拼接成字符串
+
+```
+% array=(aa bb cc dd)
+
+# 用换行符拼接
+% echo ${(F)array}
+aa
+bb
+cc
+dd
+
+# 用空格拼接
+% str=$array
+% echo $str
+aa bb cc dd
+
+# 使用其他字符或字符串拼接
+% echo ${(j:-=:)array}
+aa-=bb-=cc-=dd
+```
+
+### 字符串切分成数组
+
+```
+% str=a##b##c##d
+
+% array=(${(s:##:)str})
+% print -l $array
+a
+b
+c
+d
+```
+
+### 输出变量类型
+
+```
+# 注意如果不加 integer 或者 float,都为字符串,但计算时会自动转换类型
+% integer i=1
+% float f=1.2
+% str=abc
+% array=(a b c)
+% local -A hashmap=(k1 v1 k2 v2)
+
+% echo ${(t)i} ${(t)f} ${(t)str} ${(t)array} ${(t)hashmap}
+integer float scalar array association
+```
+
+#### 字符串、数组或哈希表嵌套取值
+
+可以嵌套多层。
+
+```
+% str=abcde
+% echo ${${str[3,5]}[3]}
+e
+
+% array=(aa bb cc dd)
+% echo ${${array[2,3]}[2]}
+cc
+# 如果只剩一个元素了,就取字符串的字符
+% echo ${${array[2]}[2]}
+b
+
+% local -A hashmap=(k1 v1 k2 v2 k3 v3)
+% echo ${${hashmap[k1]}[2]}
+1
+```
+
+### 字符串内容作为变量名再取值
+
+不需要再通过繁琐的 eval 来做这个。
+
+```
+% var=abc
+% abc=123
+
+% echo ${(P)var}
+123
+```
+
+### 对齐或截断数组中的字符串
+
+```
+% array=(abc bcde cdefg defghi)
+
+# 只取每个字符串的最后两个字符
+% echo ${(l:2:)array}
+bc de fg hi
+
+# 用空格补全字符串并且右对齐
+% print -l ${(l:7:)array}
+    abc
+   bcde
+  cdefg
+ defghi
+
+# 用指定字符补全
+% print -l ${(l:7::0:)array}
+0000abc
+000bcde
+00cdefg
+0defghi
+
+# 用指定字符补全,第二个字符只用一次
+% print -l ${(l:7::0::1:)array}
+0001abc
+001bcde
+01cdefg
+1defghi
+
+# 左对齐
+% print -l ${(r:7::0::1:)array}
+abc1000
+bcde100
+cdefg10
+defghi1
+```
+
+### 总结
+
+文中只介绍了几个比较常用的变量修饰符,还有一些没有提及,可能后续再补充。
+
+### 参考
+
+http://www.bash2zsh.com/zsh_refcard/refcard.pdf

File diff suppressed because it is too large
+ 189 - 0
Zsh-开发指南(第六篇-哈希表).md


+ 206 - 0
Zsh-开发指南(第十一篇-变量的进阶内容).md

@@ -0,0 +1,206 @@
+### 导读
+
+之前我们已经依次讲过 zsh 下的五种变量(字符串、数组、哈希表、整数、浮点数)的基本用法。但变量的使用方面,还有一些比较进阶的内容,这对一些比较特别的场景很有帮助。
+
+### typeset 命令
+
+typeset 命令用于对变量进行详细的设置。我们之前在哈希表那篇见过它。typeset -A 可以用来定义哈希表。
+
+```
+% typeset -A hashmap=(aa bb cc dd)
+```
+
+但我们后续都使用 local,因为 local 的功能和 hashmap 是一样的(除了不能用 -f 和 -g,这两个选项不常用),并且更短更容易输入。这里提到 typeset 命令,因为这个名称很好地反映了它的功能。但知道了这个后,我们可以继续使用 local 命令,毕竟它们是一样的。
+
+typeset 命令有很多选项,可以作用在变量上,起到各种各样的效果。
+
+### 强制字符串内容为小写或者大写
+
+```
+# 强制字符串内容为小写
+% local -l str=abcABC && echo $str
+abcabc
+
+# 强制字符串内容为大写
+% local -u str=abcABC && echo $str
+ABCABC
+```
+
+### 设置变量为环境变量
+
+```
+% local -x str=abc
+# 通常使用 export,功能一样
+% export str=abc
+```
+
+环境变量可以被子进程读取。
+
+### 设置变量为只读变量
+
+```
+% local -r str1=abc
+# 通常使用 readonly,功能一样
+% readonly str2=abc
+
+% str1=bcd
+zsh: read-only variable: str1
+% str2=bcd
+zsh: read-only variable: str2
+```
+
+### 设置数组不包含重复元素
+
+```
+% local -U array=(aa bb aa cc) && echo $array
+aa bb cc
+```
+
+### 设置整数的位数
+
+```
+# 如果位数不够,输出内容会用 0 补全
+% local -Z 3 i=5 && echo $i
+005
+
+# 如果超出范围会被截断
+% local -Z 3 i=1234 && echo $i
+234
+```
+
+### 进制转换
+
+设置整数为其他进制显示:
+
+```
+% local -i 16 i=255
+% echo $i
+16#FF
+```
+
+可以设置 2 到 36 之间任意进制。设置几进制显示,并不影响计算,只是显示格式不同。
+
+用 [#n] num 也可以显示十进制数为 n 进制:
+
+```
+% echo $(([#16] 255))
+16#FF
+```
+
+可以用 n#num 来显示 n 进制整数为十进制:
+
+```
+% echo $((16#ff))
+255
+```
+
+我们可以定义一系列函数来快捷地转换进制,不需要使用 bc 等外部命令:
+
+```
+0x() {
+    echo $((16#$1))
+}
+
+0o() {
+    echo $((8#$1))
+}
+
+0b() {
+    echo $((2#$1))
+}
+
+p16() {
+    echo $(([#16] $1))
+}
+
+p8() {
+    echo $(([#8] $1))
+}
+
+p2() {
+    echo $(([#2] $1))
+}
+
+
+# 其他进制转十进制
+% 0x ff
+255
+% 0b 1101
+13
+
+# 十进制转其他进制
+% p16 1234
+16#4D2
+```
+
+### 同时对多个变量赋相同的值
+
+```
+% local {i,j,k}=123
+% echo $i $j $k
+123 123 123
+```
+
+### 绑定字符串和数组
+
+```
+% local -T DIR dir
+% dir=(/a /b/c /b/d /e/f)
+% echo $DIR
+/a:/b/c:/b/d:/e/f
+
+# 删除 dir 后,DIR 也会被删除(反之亦然)
+% unset dir
+% echo $+DIR
+0
+```
+
+Linux 下经常需要处理带分隔符冒号的字符串(比如 $PATH)。如果只修改其中某一个字段,比较麻烦。local -T 可以把字符串绑定到数组上,这样直接修改数组,字符串内容也会同步变化(反之亦然)。其实在 zsh 中,$PATH 字符串就是和 $path 数组绑定的,可以直接通过修改 $path 来达到修改 $PATH 的目的,这在某些场景会方便很多。
+
+### 显示变量的定义方式
+
+```
+% array=(aa bb cc)
+% local -p array
+typeset -a array=(aa bb cc)
+
+% array+=(dd)
+% local -p array
+typeset -a array=(aa bb cc dd)
+```
+
+### 什么地方该加双引号
+
+用过 bash 的读者大概会对里边的双引号印象比较深刻,很多地方不加双引号都会出错,为了避免出错,很多人每个变量左右都加上双引号,麻烦不说,代码看起来也比较乱。
+
+其实 zsh 中已经没有那些问题了,变量两边无需加双引号,不会出现莫名其妙的错误。但有些地方还是需要加双引号的。
+
+**需要加双引号的场景:**
+
+1. 像这样的包含字符或者特殊符号的字符串 `"aa bb \t \n *"` 出现在代码中时,两边要加双引号,这个基本不需要说明。
+2. 在用 `$()` 调用命令时,如果希望结果按一个字符串处理,需要加上双引号,`"$()"`,不然的话,如果命令结果中有空格,`$()` 会被展开成多个字符串。
+3. 如果想将数组当单个字符串处理,需要加双引号,`array=(a b); print -l "$array"`。
+4. 其他的原本不是单个字符串的东西,需要转成单个字符串的场景,要加双引号。
+
+**其余情况通常都不需要加双引号,典型的情况:**
+
+1. 任何情况下,字符串变量的两边都不需要加双引号,无论里边的内容多么特殊,或者变量存不存在,都没有关系,如 `$str`。
+2. 如果不转换类型(比如数组转成字符串),任何变量的两边都不需要加双引号。
+3. `$1` `$2` `$*` 这些参数(其实它们也都是单个字符串),都不需要加双引号,无论内容是什么,或者参数是否存在。
+
+以上的 7 种情况几乎覆盖了所有场景,如果有没覆盖到的,试一下即可(让里边的内容包含空格、换行和其他特殊字符等等,看看结果是否符合预期)。
+
+### 总结
+
+本文简单介绍了一些比较使用的 typeset(或者 local)命令的用法,typeset 命令还有很多其他参数,但一般很少用,以后我也会继续更新。
+
+### 参考
+
+http://www.bash2zsh.com/zsh_refcard/refcard.pdf 
+
+http://www.linux-mag.com/id/1079/
+
+
+### 更新历史
+
+20170831:新增“什么地方该加双引号”

+ 170 - 0
Zsh-开发指南(第十七篇-使用-socket-文件和-TCP-实现进程间通信).md

@@ -0,0 +1,170 @@
+### 导读
+
+就像我之前提到的,zsh 脚本是可以直接使用 socket 文件(UNIX domain socket 所使用)或者 TCP 和其他进程通信的。如果进程都在本地,用 socket 文件效率更高些,并且不要占用端口,权限也更好控制。如果是在不同机器,可以使用 TCP。
+
+### Socket 文件
+
+UNIX domain socket 是比管道更先进的进程通信方法,是全双工的方式,并且稳定性更好。但性能比管道差一些,不过一般性能瓶颈都不会出现在这里,不用考虑性能问题。而且在一个 socket 文件上可以建立多个连接,更容易管理。另外如果通信方式从 socket 文件改成 TCP,只需要修改很少的代码(建立和关闭连接的代码稍微改一下),而从管道改成 TCP 则要麻烦很多。
+
+所以建议用 zsh 写进程交互脚本的话,直接使用 socket 文件,而不是命名管道(匿名管道就能满足需求的简单场景忽略不计)。
+
+Socket 文件的用法:
+
+```
+# 监听连接端
+# 首先要加载 socket 模块
+% zmodload zsh/net/socket
+
+% zsocket -l test.sock
+% listenfd=$REPLY
+# 此处阻塞等待连接
+% zsocket -a $listenfd
+# 连接建立完成
+% fd=$REPLY
+% echo $fd
+5
+
+# 然后 $fd 就可读可写
+% cat <&$fd
+good
+```
+
+```
+# 发起连接端
+# 首先要加载 socket 模块
+% zmodload zsh/net/socket
+
+% zsocket test.sock
+# 连接建立完成
+% fd=$REPLY
+% echo $fd
+4
+
+# 然后 $fd 就可读可写
+% echo good >&$fd
+```
+
+连接建立后,怎么用就随意了。实际使用时,要判断 fd 看连接是否正常建立了。通常使用 socket 文件要比在网络环境使用 TCP 稳定性高很多,一般不会连接中断或者出其他异常。另外可以在 zsocket 后加 -v 参数,查看详细的信息(比如使用的 fd 号)。
+
+关闭连接:
+
+```
+# 发起连接端
+# fd 是之前存放 fd 号的变量,不需要加 $
+% exec {fd}>&-
+
+# 监听连接端
+% exec {listenfd}>&-
+% exec {fd}>&-
+# 删除 socket 文件即可,如果下次再使用会重新创建,该文件不能重复使用
+% rm test.sock
+```
+
+### TCP
+
+使用 TCP 连接的方式和使用 socket 文件基本一样。
+
+```
+# 监听连接端
+# 首先要加载 tcp 模块
+% zmodload zsh/net/tcp
+
+% ztcp -l 1234
+% listenfd=$REPLY
+# 此处阻塞等待连接
+% ztcp -a $listenfd
+# 连接建立完成
+% fd=$REPLY
+% echo $fd
+3
+
+# 然后 $fd 就可读可写
+% cat <&$fd
+good
+```
+
+```
+# 发起连接端
+# 首先要加载 tcp 模块
+% zmodload zsh/net/tcp
+
+% ztcp 127.0.0.1 1234
+# 连接建立完成
+% fd=$REPLY
+% echo $fd
+3
+
+# 然后 $fd 就可读可写
+% echo good >&$fd
+```
+
+关闭连接:
+
+```
+# 发起连接端
+# fd 是之前存放 fd 号的变量
+% ztcp -c $fd
+
+# 监听连接端
+% ztcp -c $listenfd
+% ztcp -c $fd
+```
+
+### 程序样例
+
+recv_tcp,监听指定端口,并输出发送过来的消息。使用方法:recv_tcp 端口
+
+```
+#!/bin/zsh
+
+zmodload zsh/net/tcp
+
+(($+1)) || {
+    echo "Usage: ${0:t} port"
+    exit 1
+}
+
+ztcp -l $1
+listenfd=$REPLY
+
+[[ $listenfd == <-> ]] || exit 1
+
+while ((1)) {
+    ztcp -a $listenfd
+    fd=$REPLY
+    [[ $fd == <-> ]] || continue
+
+    cat <&$fd
+    ztcp -c $fd
+}
+```
+
+send_tcp,用来向指定机器的指定端口发一条消息。使用方法:send_tcp 机器名  端口 消息 (机器名可选,如果没有则发到本机,消息可以包含空格)
+
+```
+#!/bin/zsh
+
+zmodload zsh/net/tcp
+
+(($# >= 2)) || {
+    echo "Usage: ${0:t} [hostname] port message"
+    exit 1
+}
+
+if [[ $1 == <0-65535> ]] {
+    ztcp 127.0.0.1 $1
+} else {
+    ztcp $1 $2
+    shift
+}
+
+fd=$REPLY
+[[ "$fd" == <-> ]] || exit 1
+
+echo ${*[2,-1]} >&$fd
+ztcp -c $fd
+```
+
+### 总结
+
+本文介绍了使用 socket 文件或者 TCP 来实现两个脚本之间通信的方法。

File diff suppressed because it is too large
+ 211 - 0
Zsh-开发指南(第十三篇-管道和重定向).md


+ 178 - 0
Zsh-开发指南(第十二篇-[[-]]-的用法).md

@@ -0,0 +1,178 @@
+### 导读
+
+[[ ]] 是我们比较熟悉的符号了,从第一篇开始我们就一直在用,但我一直没有详细介绍它的用法,只用到了它的一小部分功能。本文详细介绍 [[ ]] 的用法。
+
+### 比较字符串
+
+[[ ]] 最常用的功能之一是比较字符串,这也是我们一直在用的功能。
+
+```
+# 匹配
+% [[ abc == abc ]] && echo good
+good
+
+# = 和 == 是一样的,最好统一使用一种
+% [[ abc = abc ]] && echo good
+good
+
+# 不匹配
+% [[ abc != abd ]] && echo good
+good
+
+# 正则表达式匹配
+% [[ abc =~ a.c ]] && echo good
+good
+
+# 前者字符序比后者小
+% [[ abc < bcd ]] && echo good
+good
+
+# 前者字符序比后者大
+% [[ cde > bcd ]] && echo good
+good
+
+# 没有 >= 和 <=
+% [[ cde >= bcd ]] && echo good
+zsh: parse error near `bcd'
+```
+
+除了在里边用等号、不等号之类比较外,还可以判断字符串是否为空:
+
+```
+% str=abc
+# 判断字符串内容长度是否大于 0,等同于 (($#str))
+% [[ -n "$str" ]] && echo good
+good
+
+% str=""
+# 判断字符串是否为空,等同于 ((! $#str))
+% [[ -z "$str" ]] && echo good
+good
+```
+
+但这两种用法,我们都有更方便的其他实现方法,没有必要用它们。
+
+### 判断文件
+
+[[ ]] 另一类很重要的功能是判断文件,比如判断某一个文件是否存在、是否是目录、是否可读等等。
+
+判断 /bin/zsh 文件是否存在:
+
+```
+% [[ -e /bin/zsh ]] && echo good
+good
+% [[ -e /bin/zshh ]] && echo good
+
+```
+
+-e 可以替换成如下的选项,用法是一致的:
+
+选项 | 符号条件的文件
+-- | --
+-b | 块设备文件
+-c | 字符设备文件
+-d | 目录
+-e | 存在的任何文件
+-f | 普通文件,含符号链接,不含目录、设备文件、socket、FIFO
+-g | 设置了 setgid 的文件
+-h | 符号链接
+-k | 设置了粘滞位(sticky bit)的文件
+-p | FIFO 文件
+-r | 对当前进程可读的文件
+-s | 非空文件
+-u | 设置了 setuid 的文件
+-x | 对当前进程可执行的文件
+-w | 对当前进程可写的文件
+-L | 符号链接(同 -h)
+-O | 被当前进程的用户拥有的文件
+-G | 被当前进程的用户组拥有的文件
+-S | socket 文件
+-N | atime 和 mtime 一样的文件
+
+还有一个比较特殊的 -t 选项:
+
+```
+# $$ 是当前的进程 id
+% ls /proc/$$/fd
+0  1  10  11  2
+% [[ -t 10 ]] && echo good
+good
+% [[ -t 3 ]] && echo good
+
+```
+
+-t 后要接数字(如果不是,相当于 0),判断当前进程是否打开了对应的 fd(进程默认会打开 0、1、2 这三个 fd,分别对应标准输入、标准输出和错误输出,此外每打开一个文件、管道或者网络连接,都会对应一个 fd,关掉后对应 fd 会消失)。
+
+### 比较文件
+
+除了判断单个文件是否符合条件外,[[ ]] 还可以用来比较两个文件。
+
+```
+# file1 比 file2 新
+% [[ file1 -nt file2 ]]
+
+# file1 比 file2 旧
+% [[ file1 -ot file2 ]]
+
+# file1 和 file2 是否对应同一个文件(路径相同或者互为硬连接)
+% [[ file1 -ef file2 ]]
+```
+
+### 比较数值
+
+[[ ]] 也可以用来比较数值,注意不是用等号、大于号、小于号等等比较,有一系列专门的符号。通常我们没必要用 [[ ]] 来比较数值,用 (( )) 更方便一些。
+
+```
+# -eq 是判断两个数值是否相等
+% [[ 12 -eq 12 ]] && echo good
+good
+```
+
+-eq 可以替换成下列符号,用法一样:
+
+符号 | 含义
+-- | --
+-eq | 相等
+-ne | 不相等
+-lt | <
+-gt | >
+-le | <=
+-ge | >=
+
+### 组合使用
+
+```
+# && 是逻辑与
+% [[ a == a && b == b ]] && echo good
+good
+
+# || 是逻辑或
+%  [[ a == a || a == b ]] && echo good
+good
+
+# ! 是逻辑非
+% [[ ! a == b ]] && echo good
+good
+
+# 可以一起用,! 优先级最高,其次 &&,再次 ||
+% [[ ! a == b && b == a || b == b ]] && echo good
+good
+
+# 如果不确定优先级,可以加小括号
+% [[ ((! a == b) && b == a) || b == b ]] && echo good
+good
+```
+
+需要注意一下空格,[[ ]] 内侧和内容之间需要空格隔开,== 两边也需要空格。如果是在 zsh 中直接敲入,! 后边也要加一个空格,不然会被解析成历史命令。
+
+### [ ] 符号
+
+除了 [[ ]] 符号,[ ] 符号(它是古老的 test 命令化身)也可以用来判断字符串、文件、数值等等,但功能没有 [[ ]] 全,只支持上边列的一部分功能(不支持 ==、=~、>、<、(、) ,并且逻辑与或的语法不一样,不能调整优先级,用起来很不方便),通常没有必要使用 [ ](如需使用,可以 man test 查看用法)。
+
+### 总结
+
+本文详细介绍了 [[ ]] 的用法,基本覆盖全面了。
+
+### 参考
+
+http://www.bash2zsh.com/zsh_refcard/refcard.pdf

File diff suppressed because it is too large
+ 147 - 0
Zsh-开发指南(第十五篇-进程与作业控制).md


+ 1 - 0
Zsh-开发指南(第十八篇-clone-和-exec-的用法).md

@@ -0,0 +1 @@
+

File diff suppressed because it is too large
+ 82 - 0
Zsh-开发指南(第十六篇-alias-和-eval-的用法).md


+ 294 - 0
Zsh-开发指南(第十四篇-文件读写).md

@@ -0,0 +1,294 @@
+### 导读
+
+之前我们也偶尔接触过读写文件的方法,本篇会系统讲读写文件的各种方法。
+
+### 写文件
+
+写文件要比读文件简单一些,最常用的用法是使用 > 直接将命令的输出重定向到文件。如果文件存在,内容会被覆盖;如果文件不存在,会被创建。
+
+```
+% echo abc > test.txt
+```
+
+如果不想覆盖之前的文件内容,可以追加写入:
+
+```
+% echo abc >> test.txt
+```
+
+这样如果文件存在,内容会被追加写入进去;如果文件不存在,也会被创建。
+
+#### 创建文件
+
+有时我们只想先创建个文件,等以后需要的时候再写入。
+
+touch 命令用于创建文件(普通文件):
+
+```
+% touch test1.txt test2.txt
+
+# 或者用 echo 输出重定向,效果和 touch 一样
+# 加 -n 是因为不加的话 echo 会输出一个换行符
+% echo -n >>test1.txt >>test2.txt
+
+# 或者使用输入重定向
+% >>test1.txt >>test2.txt </dev/null
+
+# mkdir 用来创建目录,如果需要在新目录创建文件
+% mkdir dir1 dir2
+```
+
+如果文件已经存在,touch 命令会更新它的时间(mtime、ctime、atime 一起更新,其余两种方法不会)到当前时间。另外下边的清空文件方法,也都可以用来创建文件。touch 命令的使用比较方便,但如果想尽量少依赖外部命令,可以使用后两种方法。
+
+因为文件创建过程通常不存在性能瓶颈,不用过多考虑性能因素。如果需要创建大量文件,可以在自己的环境分别用这几种方法试验几次,看需要多少时间。
+
+我在树莓派 3B 简单测试一下:
+
+```
+# 三个脚本,分别创建 1000 个文件
+% cat test1 test2 test3
+#!/bin/zsh
+
+touch test1{1..1000}.txt
+#!/bin/zsh
+
+echo -n >>test2{1..1000}.txt
+#!/bin/zsh
+
+>>test3{1..1000}.txt </dev/null
+```
+
+```
+# 运行了几次,结果差不多
+% time ./test1; time ./test2; time ./test3
+./test1  0.02s user 0.03s system 86% cpu 0.058 total
+./test2  0.02s user 0.02s system 70% cpu 0.056 total
+./test3  0.03s user 0.01s system 72% cpu 0.055 total
+```
+
+另外如果文件数量太多的话,方法二、三要按批次创建,因为一个进程能打开的 fd 总数是有上限的。
+
+#### 清空文件
+
+有时我们需要清空一个现有的文件:
+
+```
+# 使用 echo 输出重定向
+% echo -n >test.txt
+
+# 使用输入重定向
+% >test.txt </dev/null
+
+# 也可以使用 truncate 命令清空文件
+% truncate -s 0 test.txt
+```
+
+通常使用第一种方法即可,比较简单易懂。非特殊场景尽量不要用像 truncate 这样不常见的命令。
+
+#### 删除文件
+
+删除文件的方法比较单一,用 rm 命令即可。
+
+```
+% rm test1.txt test2.txt
+
+# -f 参数代表即使文件不存在也不报错
+% rm -f test1.txt test2.txt
+
+# -r 参数可以递归删除目录和文件
+% rm -r dir1 dir2 test*.txt
+
+# -v 参数代表 rm 会输出删除文件的过程
+% rm -v test*.txt
+removed 'test1.txt'
+removed 'test2.txt'
+```
+
+删除文件必须借助 rm 命令。如果一定要不依赖外部命令的话,zsh/files 模块里也有一个 rm 命令,可以用 zmodload zsh/files 加载,然后 rm 就变成了内部命令,用法基本相同。
+
+```
+% zmodload zsh/files
+% which -a rm
+rm: shell built-in command
+/usr/bin/rm
+```
+
+此外 zsh/files 中还有内置的 chgrp、chown、ln、mkdir、mv、rmdir、sync 命令。如果不想依赖外部命令,或者系统环境出问题了用不了外部命令,可以使用这些。这可以作为命令不存在或者因为命令本身问题执行异常的一个 fallback 方案,来提高脚本的健壮性。
+
+#### 多行文本写入
+
+通常我们写文件时不会每一行都单独写入,这样效率太低。
+
+可以先把字符串拼接起来,然后一次性写入,这样比多次写入效率更高:
+
+```
+% str=ab
+% str+="\ncd"
+% str +="\n$str"
+
+echo $str > test.txt
+```
+
+可以直接把数组写入到文件,每行一个元素:
+
+```
+% array=(aa bb cc)
+
+% print -l $array > test.txt
+```
+
+如果是将一段内容比较固定的字符串写入到文件,可以这样:
+
+```
+# 在脚本中也是如此,第二行以后的行首 > 代表换行,非输入内容
+# <<EOF 代表遇到 EOF 时会终止输入内容
+# 里边也可以使用变量
+% > test.txt <<EOF
+> aa
+> bb
+> cc dd
+> ee
+> EOF
+
+% cat test.txt
+aa
+bb
+cc dd
+ee
+```
+
+#### 用 mapfile 读写文件
+
+如果不喜欢使用重定向符号,还可以用哈希表来操作文件。Zsh 有一个 zsh/mapfile 模块,用起来很方便:
+
+```
+% zmodload zsh/mapfile
+
+# 这样就可以创建文件并写入内容,如果文件存在则会被覆盖
+% mapfile[test.txt]="ab cd"
+% cat test.txt
+ab cd
+
+# 判断文件是否存在
+% (($+mapfile[test.txt])) && echo good
+good
+
+# 读取文件
+% echo $mapfile[test.txt]
+ab cd
+
+# 删除文件
+% unset "mapfile[test.txt]"
+
+# 遍历文件
+% for i (${(k)mapfile}) {
+> echo $i
+> }
+test1.txt
+test2.txt
+```
+
+#### 从文件中间位置写入
+
+有时我们需要从一个文件的中间位置(比如从第 100 的字符或者第三行开始)继续写入,覆盖之后的内容。Zsh 并不直接提供这样的方法,但我们可以迂回实现,先用 truncate 命令把文件截断,然后追加写。如果文件后边的内容还需要保留,可以在截断之前先读取进来(见下文读文件部分的例子),最后再写回去。
+
+```
+% echo 1234567890 > test.txt
+# 只保留前 5 个字符
+% truncate -s 5 test.txt
+% cat test.txt
+12345 
+% echo abcde >> test.txt
+% cat test.txt
+12345abcde
+```
+
+### 读文件
+
+#### 读取整个文件
+
+读取整个文件比较容易:
+
+```
+% str=$(<test.txt)
+% echo $str
+aa
+bb
+cc dd
+ee
+```
+
+#### 按行遍历文件
+
+如果文件比较大,那读取整个文件会消耗很多资源,可以按行遍历文件内容:
+
+```
+% while {read i} {
+> echo $i
+> } <test.txt
+aa
+bb
+cc dd
+ee
+```
+
+read 命令是从标准输入读取一行内容,把标准输入重定向后,就变成了从文件读取。
+
+#### 读取指定行
+
+如果只需要读取指定的某行或者某些行,不需要用上边的方法加自己计数。
+
+```
+# (f)2 是读取第二行
+% echo ${"$(<test.txt)"[(f)2]}
+bb
+```
+
+#### 读取文件到数组
+
+读取文件内容到数组中,每行是数组的一个元素:
+
+```
+% array=(${(f)"$(<test.txt)"})
+```
+
+#### 读取指定数量的字符
+
+有时我们需要按字节数来读取文件内容,而不是按行读取。
+
+```
+% cat test.txt
+1234567890
+# -k5 是只最多读取 5 个字节,-u 0 是从 fd 0 读取,不然会卡住
+% read -k 5 -u 0 str <test.txt
+% echo $str
+12345
+```
+
+#### 向文件中间插入内容
+
+有时我们会遇到比较麻烦的场景,在某个文件中间插入一些内容,而前后的内容保持不变。
+
+Zsh 并没有直接提供这样的功能,但我们可以迂回实现。
+
+```
+% echo -n 1234567890 > test.txt
+# 先全部读进来
+% str=$(<test.txt)
+# 截断文件
+% truncate -s 5 test.txt
+# 插入内容
+% echo -n abcde >> test.txt
+# 将后半部分文件追加回去
+% echo -n $str[6,-1] >> test.txt
+% cat test.txt
+12345abcde67890
+```
+
+但如果比较比较大的话,就不能将整个文件全部读进来,可以先在循环里用 read -k num 一次读固定数量的字符,然后写入一个中间文件,然后再 truncate 原文件,插入内容。最后再 cat 中间文件 >> 原文件 追加原来的后半部分内容即可。
+
+另外这种从文件中间写入或者读取内容的场景,都可以使用 dd 命令实现,可以自行搜索 dd 命令的用法。
+
+### 总结
+
+本文比较详细地介绍了各种读写文件的方法,基本可以覆盖常用的场景。

File diff suppressed because it is too large
+ 277 - 0
Zsh-开发指南(第十篇-文件查找和批量处理).md


+ 116 - 0
Zsh-开发指南(第四篇-字符串处理之通配符).md

@@ -0,0 +1,116 @@
+### 导读
+
+这是字符串处理系列的第三篇文章。前两篇基本覆盖了字符串处理中的常用操作,但在字符串匹配方面,没有详细展开。
+
+通配符(glob)是 shell 中的一个比较重要的概念,可以认为是正则表达式的简化版本。通配符在字符串匹配和文件名搜索等方面非常有用。本篇只讲它在字符串匹配上的用法。
+
+### 通配符的基本用法
+
+之前在讲字符串匹配判断时,通配符出现过,就是 `*$str"*` 两边的星号。
+
+```
+% str1=abcd
+% str2=bc
+
+# 星号要在引号外边
+% [[ "$str1" == *"$str2"* ]] && echo good
+good
+
+# 注意带通配符的字符串必须放在右边
+% [[ *"$str2"* == "$str1" ]] && echo good
+
+```
+
+星号是最常用的通配符,用于匹配任意数量(包括 0 个)的任意字符。
+
+```
+# 问号用于匹配一个任意字符
+% [[ abcd == ab?? ]] && echo good
+good
+
+# 中括号用于匹配出现在其中的单个字符
+% [[ abcd == abc[bcd] ]] && echo good
+good
+
+# 如果中括号里第一个字符是 ^,则匹配除了除了中括号里的单个字符
+% [[ abcd == abc[^de] ]] && echo good
+% [[ abcd == abc[^ce] ]] && echo good
+good
+
+# 中括号里可以指定字符的范围
+% [[ a4 == [a-b][2-5] ]] && echo good
+good
+
+# 可以指定多个字符范围,并且可以掺杂其他字符
+% [[ B4 == [a-cdddA-B][2-5] ]] && echo good
+good
+
+# 尖括号用于匹配一定范围的单个整数
+% [[ 123 == 12<3-4> ]] && echo good
+good
+
+# 可以匹配整个整数
+% [[ 123 == <100-200> ]] && echo good
+good
+
+# 可以没有上下界,默认的下界是 0,上界是正无穷
+% [[ 123 == <100-> && 123 == <-200> ]] && echo good
+good
+
+# 可以上下界都没有,那么会匹配任意正整数和 0
+# 这个可以用来判断字符串是否构成整数
+# [[ 123 == <-> ]] && echo good
+good
+
+# ( 1 | 2 | ... ) 用于同时判断多个条件,满足一个即可
+%  [[ ab == (aa|ab) ]] && echo good
+good
+
+# 如果中括号里要用 - 或者 ^,放在最后即可,不需要转义
+% [[ -^3 == [a-c-][3^-][3^-] ]] && echo good
+good
+
+```
+
+以上是通配符的基本用法,总结一下。
+
+通配符          | 含义                                          |
+--------------- | --------------------------------------------- |
+\*              | 任意数量的任意字符                            |
+?               | 任意一个字符                                  |
+[abcd]          | abcd 中的任意一个字符                         |
+[^abcd]         | 除 abcd 外的任意一个字符                      |
+[a-c]           | a 和 c 之间的一个字符                         |
+[a-cB-Dxyz]     | a 和 c、B 和 D 之间以及 xyz 中的一个字符      |
+<1-100>         | 1 和 100 之间的整数                           |
+<-50>           | 0 和 50 之间的整数                            |
+<100->          | 大于 100 的整数                               |
+<->             | 任意正整数和 0                                |
+([a-c]\|<1-100>) | a 和 c 之间的一个字符或者 1 和 100 之间的整数 |
+
+### 加强版通配符
+
+Zsh 还支持加强版通配符,功能更多一些。如果使用加强版的通配符,需要先在代码里加上 `setopt EXTENDED_GLOB`。
+
+通配符 | 含义 | 匹配的样例
+-- | -- | --
+^abc | 除了 abc 外的任意字符串 | aaa
+abc^abc | 以 abc 开头,但后边不是 abc 的字符串 | abcabd
+a*c~abc | 符合 a*c 但不是 abc 的字符串 | adc
+a# | 任意数量(包括 0)个 a | aaa
+b## | 一个或者多个 b | b
+(ab)## | 一个或者多个 ab | abab
+(#i)abc | 忽略大小写的 abc | AbC
+(#i)ab(#I)c | 忽略大小写的 ab 接着 c | ABc
+(#l)aBc | a 和 c 忽略大小写,但 B 必须大写 的 aBc | aBC
+(#a1)abc | 最多错(多或缺也算)一个字符的 abc | a2c 或 ab 或 abcd
+
+此外还有一些更高级的用法,暂时先略过。
+
+### 总结
+
+字符串的内容先告一段落,但之后的文章依然会不断地涉及字符串,因为数组和哈希表里的内容通常是字符串,处理目录文件时也涉及大量的字符串操作等等,届时会有新的字符串处理方法。此外,如果我发现新的处理字符串的方法或者技巧,也会更新这几篇文章。
+
+### 参考
+
+http://www.bash2zsh.com/zsh_refcard/refcard.pdf