Zsh 開發指南(第三篇 字串處理之轉義字元和格式化輸出)

陌辭寒發表於2019-02-19

導讀

上一篇講了 zsh 的常用字串操作,這篇開始講更為瑣碎的轉義字元和格式化輸出相關內容。包括轉義字元、引號、printprintf 的使用等等。其中很多內容沒有必要記憶,作為手冊參考即可。

轉義字元

轉義字元是很多程式語言中都有的概念,它主要解決某些字元因為沒有對應鍵盤按鍵無法直接輸出、字元本身有特殊含義(比如 ")或者顯示不直觀(比如難以區別多個空格和一個 tab)等問題。

最常用的轉義字元是
(換行)、
(回車)、 (tab)。

直接用 echoprint 或者 printf 內建命令都可以正常輸出轉義字元,但包括轉義字元的字串需要用引號(單雙引號都可以)擴起來。

% echo `Hello
	World`
Hello
        World複製程式碼

常用轉義字元對照表,不常用的可以去查 ASCII 碼錶,然後使用 xnn(如 x14)。

轉義字元 含義 ASCII 碼值(十六進位制)
換行 0a
回車 0d
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 是類似 echo 的內部命令(echo 命令很簡單,不作介紹),但功能比 echo 強大很多。完全可以使用 print 代替 echo

不加引數的 printecho 的功能基本一樣,但如果字串裡包含轉義字元,某些情況可能不一致。如果需要輸出轉義字元,儘量統一使用 print,避免不一致導致的麻煩。

% print `Line	one
Line	two`
Line    one
Line    two

# echo 的輸出和 print 不一致
% echo `Line	one
Line	two`
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 選項列表”方便查詢。

-l 用於分行輸出字串:

# 每個字串一行,字串列表是用空格隔開的
% print -l aa bb
aa
bb

# 也可以接陣列,陣列相關的內容之後會講到
# 命令後的多個字串都可以用陣列取代,效果是相同的
% array=(aa bb)
% print -l $array
aa
bb複製程式碼

-n 用於不在輸出內容的末尾自動新增換行符(echo 命令也有這個用法):

% print abc
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 `
`

複製程式碼

-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 `		abc` | hexdump -C
00000000  20 20 20 20 61 62 63 0a                           |    abc.|
00000008

% print -x 4 `		abc` | 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		abc` | hexdump -C
00000000  61 62 20 20 20 20 61 62  63 0a                    |ab    abc.|
0000000a

% print -X 4 `ab		abc` | 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%

# 取小數點後 1 位
% printf "%.1f" 12.34
12.3

# 科學計數法輸出浮點數
% printf "%e" 12.34
1.234000e+01

# 將十進位制數字轉成十六進位制輸出
% printf "%x" 12
c

# 補齊空格或者補齊 0
% printf "%5d
%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 可以方便地輸出帶顏色和特殊樣式的字串,不用再和

相關文章