zsh 的字符串处理功能,要比绝大多数编程语言自带的字符串函数库或者类库要强大(在不依赖外部命令的情况)。同时各种用法也比较怪异,很多时候简洁性和可读性是有矛盾的,很难兼顾。而 shell 的使用场景决定简洁性是不能被牺牲掉的,即使用 Python 这样比较简洁的语言来处理字符串,很多时候也只能写出冗长的代码,而 zsh 经常可以一行搞定(可能有人想到了 Perl,Perl 在处理文本方面确实有比较明显的优势,但使用 Perl 的话也要承担更多的成本),如果再加上适当地使用外部命令,基本可以应付大多数字符串处理场景。因为字符串处理的内容比较丰富,我会分多篇文章写。本篇只涉及最基础和常用的字符串操作,包括字符串的拼接、切片、截断、查找、遍历、替换、匹配、大小写转换、分隔等等。
字符串长度
1 2 3 4 5 6
| % str=abcde % echo $#str 5 % echo $#1
|
字符串拼接
1 2 3 4 5 6 7 8 9
| % str1=abc % str2=def % str2+=$str1 % echo $str2 defabc % str3=$str1$str2 abcdefabc
|
字符串切片
1 2 3 4 5 6 7 8
| % str=abcdef % echo $str[2,4] bcd % echo $str[2,-1] bcdef echo ${1[2,4]}
|
字符串切片还有另一种风格的方法,即 bash 风格,功能大同小异。通常没有必要用这个,而且因为字符位置是从 0 开始算,容易混淆。
1 2 3 4 5
| % str=abcdef % echo ${str:1:3} bcd % echo ${str:1:-1} bcde
|
字符串截断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| % str=abcdeabcde % echo ${str#*b} cdeabcde % echo ${str%d*} abcdeabc % echo ${str##*b} cde % echo ${str%%d*} abc
|
字符串查找
子字符串定位。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| % str=abcdef % echo $str[(I)cd] 3 % (($str[(I)cd])) && echo good good % echo $str[(I)cdd] 0 % echo $str[(i)cd] 3 % echo $str[(i)cdd] 7
|
遍历字符
1 2 3 4 5 6 7 8 9
| % str=abcd % for i ({1..$#str}) { > echo $str[i] >} a b c d
|
字符串替换
按内容替换和删除字符。
1 2 3 4 5 6 7 8 9 10 11 12
| % str=abcdefg % str[2]=1 % echo $str a1cdefg % str[2,3]=2345 % echo $str a2345defg
|
判断字符串变量是否存在
如果用 [[ "$strxx" == "" ]]
,那无法区分变量是没有定义还是内容为空,在某些情况是需要区分二者的。
1 2 3 4 5
| % (($+strxx)) && echo good % strxx="" % (($+strxx)) && echo good good
|
(($+var))
的用法也可以用来判断其他类型的变量,如果变量存在则返回真(0),否则返回假(1)。
字符串匹配判断
判断是否包含字符串
1 2 3 4 5
| % str1=abcd % str2=bc % [[ $str1 == *$str2* ]] && echo good good
|
正则表达式匹配
1 2 3 4 5 6 7
| % str=abc55def % [[ $str =~ "c[0-9]{2}\de" ]] && echo a a
|
大小写转换
1 2 3 4 5 6 7 8 9 10 11 12 13
| % str="ABCDE abcde" % echo ${(U)str} --- ${str:u} ABCDE ABCDE --- ABCDE ABCDE % echo ${(L)str} --- ${str:l} abcde abcde --- abcde abcde % echo ${(C)str} Abcde Abcde
|
目录文件名截取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| % filepath=/a/b/c.x % echo ${filepath:h} /a/b % echo ${filepath:t} c.x % echo ${filepath:e} x % echo ${filepath:r} /a/b/c
|
字符串分隔
1 2 3 4 5 6 7 8 9 10 11 12
| % str='aa bb cc dd' % echo ${str[(w)2]} bb % echo ${str[(w)3]} cc % str='aa--bb--cc' % echo ${str[(ws:--:)3]} cc
|
多行字符串
1 2 3 4 5
| % str="line1 > line2" % echo $str line1 line2
|
读取文件内容到字符串
1 2 3 4 5 6 7 8 9 10
| str=$(<filename) echo "$(<filename)" for i (${(f)"$(<filename)"}) { echo $i }
|
读取文件指定行
文件 test.txt 内容如下:
1 2
| line 1. apple line 2. orange
|
1 2 3 4 5 6 7 8 9 10
| % echo ${"$(<test.txt)"[(f)2]} line 2. orange % echo ${"$(<test.txt)"[(fr)*ang*]} line 2. orange echo ${"$(<test.txt)"[(fr)*pp*]#line}
|
读取进程输出到字符串
读进程输出和读文件类似。
上边字符串相关的处理,直接把 $(<test.txt)
换成 $(命令)
即可。如果一定需要一个文件名,可以这样。
1 2 3 4 5 6 7
| % wc -l <(ps) 4 /proc/self/fd/11 % wc -l =(ps) 3 /tmp/zshMWDpqD
|
参考
zsh开发指南