### 导读 之前我们已经依次讲过 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:新增“什么地方该加双引号”