导读
上一篇讲了 zsh 的常用字符串操作,这篇开始讲更为琐碎的转义字符和格式化输出相关内容。包括转义字符、引号、print
、printf
的使用等等。其中很多内容没有必要记忆,作为手册参考即可。
转义字符
转义字符是很多编程语言中都有的概念,它主要解决某些字符因为没有对应键盘按键无法直接输出、字符本身有特殊含义(比如 \
、"
)或者显示不直观(比如难以区别多个空格和一个 tab)等问题。
最常用的转义字符是 \n
(换行)、\r
(回车)、\t
(tab)。
直接用 echo
、print
或者 printf
内置命令都可以正常输出转义字符,但包括转义字符的字符串需要用引号(单双引号都可以)扩起来。
1 2 3
| % echo 'Hello\n\tWorld' Hello World
|
常用转义字符对照表,不常用的可以去查 ASCII 码表,然后使用 \xnn
(如 \x14
)。
转义字符 |
含义 |
ASCII 码值(十六进制) |
\n |
换行 |
0a |
\r |
回车 |
0d |
\t |
tab |
09 |
\ |
\ |
5c |
` |
` |
60 |
\xnn |
取决于 nn |
nn |
可以用 hexdump
命令查看字符的 ASCII 码值。
1 2 3
| % echo ab= | hexdump -C 00000000 61 62 3d 0a |ab=.| 00000004
|
还有一些字符是可选转义(通常有特殊含义的字符都是如此)的,比如空格、"
、'
、*
、~
、$
、&
、(
、)
、[
、]
、{
、}
、;
、?
等等,即如果在引号里边则无需转义(即使转义也不出错,转义方法都说前边加一个 \
),但如果在引号外边则需要转义。谨慎起见,包含半角符号的字符串全部用引号包含即可,可以避免不必要的麻烦。
可以这样检查一个字符在空格外是否需要转义,输出的字符中前边带 \
的都是需要的。
1 2 3 4 5
| % str='~!@#$%^&*()_+-={}|[]:;<>?,./"' % print -r ${(q)str} \~\!@\
|
单引号
单引号的左右主要是为了避免字符串里的特殊字符起作用。在单引号中,只有一个字符需要转义,转义符号 \
。所以如果字符串里包含特殊符号时,最好使用单引号包含起来,避免不必要的麻烦。如果字符串需要包含单引号,可以使用这几种方法。
1 2 3 4 5 6 7 8 9 10 11
| % echo "a'b" a'b # 用转义符号 % echo a\'b a'b # 同时使用单引号和转义符号,用于包含单引号和其他特殊符号的场景 % echo 'a"\'\''b*?' a"\'b*?
|
双引号
双引号的作用类似单引号,但没有单引号那么严格,有些特殊字符在双引号里可以继续起作用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| % 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
|
简单说,$
加各种东西的用法在双引号里都是可以正常使用的,而其他特殊符号(比如 *
、?
、>
)的功能通常不可用。
反引号
反引号是用来运行命令的,它会返回命令结果,以便保存到变量等等。
1 2 3 4 5 6 7 8 9 10
| % str=`ls` % echo $str git tmp % str=$(ls) % echo $str git tmp
|
反引号的功能和 $( )
功能基本一样,但 $( )
可以嵌套,而反引号不可以,而且反引号看起来更费事,某些字体中的反引号和单引号差别不大。所以在脚本里不建议使用反引号。
print 命令用法
print
是类似 echo
的内部命令(echo
命令很简单,不作介绍),但功能比 echo
强大很多。完全可以使用 print
代替 echo
。
不加参数的 print
和 echo
的功能基本一样,但如果字符串里包含转义字符,某些情况可能不一致。如果需要输出转义字符,尽量统一使用 print
,避免不一致导致的麻烦。
1 2 3 4 5 6 7 8
| % print 'Line\tone\n\Line\ttwo' Line one Line two % echo 'Line\tone\n\Line\ttwo' Line one \Line two
|
print
有很多参数,在 zsh 里输入 print -
然后按 tab 即可查看选项帮助(如果没有效果,需要配置 ~/.zshrc
里的补全选项,网上有很多现成的配置)。
1 2 3 4 5 6 7
| # - 后直接按 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
用于分行输出字符串:
1 2 3 4 5 6 7 8 9 10 11
| % print -l aa bb aa bb % array=(aa bb) % print -l $array aa bb
|
-n
用于不在输出内容的末尾自动添加换行符(echo
命令也有这个用法):
1 2 3 4 5
| % print abc abc % print -n abc abc%
|
-m
用于只输出匹配到的字符串:
1 2
| % print -m "aa*" aabb abc aac aabb aac
|
-o/-O/-i
用于对字符串排序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| % print -o a d c 1 b g 3 s 1 3 a b c d g s % print -O a d c 1 b g 3 s s g d c b a 3 1 % print -oi A B C a c A B C A a A B B C c C % print -o A B C a c A B C a A A B B c C C
|
-r
用于不对字符串进行转义。print
默认是会对转义字符进行转义的,加 -r
后会原样输出:
-c
用于将字符串按列输出。如果对自动决定的列数不满意,可以用 -C
指定列数:
1 2 3
| % 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
用于按指定列数输出字符串:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| % 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 % print -a -C 3 a bb ccc dddd ee f g a bb ccc dddd ee f g
|
-D
用于将符合条件的路径名转化成带 ~ 的格式(~ 是家目录):
1 2 3 4 5 6
| % print -D /home/goreliu/git ~/git % print -D /mnt/c/mine ~mine
|
-N
用于将输出的字符串以 \x00
(null)分隔,而不是空格。这样可能方便处理包含空格的字符串,xargs
等命令也可以接受以 \x00
分隔的字符串:
1 2 3 4 5 6
| % 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 对应的空格数:
1 2 3 4 5 6 7
| % 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 不能凭空消失,一定要转成至少一个空格才行。如果没理解就自己多试试找规律吧。
1 2 3 4 5 6 7
| % 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
查看。
1 2 3 4 5
| % print -u 2 good good % print good >&2
|
-v
用于把输出内容保存到变量:
1 2 3 4
| % print -v str aa bb cc % echo $str aa bb cc
|
-s/-S
用于把字符串保存到历史记录:
1 2 3 4 5 6 7 8
| % print -s ls -a % history | tail -n 1 2222 ls -a % print -S "ls -a" % history | tail -n 1 2339 ls -a
|
-z
用于把字符串输出到命令行编辑区:
1 2 3
| % print -z aa bb cc % aa bb cc_
|
-f
用于按指定格式化字符串输出,同 printf
,用法见“printf
命令用法”。
-P
用于输出带颜色和特殊样式的字符串,见“输出带颜色和特殊样式的字符串”。
-b
用于辨认出 bindkey 中的转义字符串,bindkey 是 Zle 的快捷键配置内容,写脚本用不到,不作介绍。
-R
用于模拟 echo
命令,只支持 -n
和 -e
选项,通常用不到。
printf 命令用法
printf
命令很像 c 语言的 printf
函数,用于输出格式化后的字符串:
1 2 3 4 5
| % printf ":%d %f:" 12 34.56 :12 34.560000:%
|
printf
的第一个参数是格式化字符串,在 zsh 里输入 printf %
后按 tab,可以看到所有支持的用法。下面只举几个比较常用的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| % 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 % printf "%5d\n%05d" 12 12 12 00012
|
我把完整的格式贴在这里,方便搜索:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| -- 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
之类莫名其妙的字符串打交道了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| % print -P '%B%F{red}abc' 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 |
把内容放置到命令行编辑区 |
无 |
参考
zsh开发指南