shell基礎——字符串處理(轉載)

Shell的字符串處理
 
1 獲得長度
  %x="abcd"
  #方法一
      %expr length $x
      4
  # 方法二
      %echo ${#x}
      4
  # 方法三
      %expr "$x" : ".*"
      4
  # expr 的幫助
  # STRING : REGEXP   anchored pattern match of REGEXP in STRING
2 查找子串
    %expr index  $x "b"
     2
    %expr index  $x "a"
    1
    %expr index  $x "b"
    2
    %expr index  $x "c"
    3
    %expr index  $x "d"
    4
3 獲得子字符串
  # 方法一
    # expr <string> startpos length
    %expr substr "$x" 1 3
    abc
    %expr substr "$x" 1 5
    abcd
    %expr substr "$x" 2 5
    bcd
  # 方法二
    # ${x:pos:lenght}
    %echo ${x:1}
    bcd
    %echo ${x:2}
    cd
    %echo ${x:0}
    abcd
    %echo ${x:0:2}
    ab
    %pos=1
    %len=2
    %echo ${x:$pos:$len}
    bc
 
  # 方法三
   
    %echo $x | cut -c 1-3
    abc
 
4 匹配正則表達式
   %expr match $x "."
   1
   %expr match $x "abc"
   3
   %expr match $x "bc"
   0
   expr "2009/08/10" : "[0-9]\{4\}/[0-9]\{2\}/[0-9]\{2\}$"
   10
5 字符串的掐頭去尾
   %x=aabbaarealwwvvww
   %echo "${x%w*w}"
   aabbaarealwwvv
   %echo "${x%%w*w}"
   aabbaareal
   %echo "${x##a*a}"
   lwwvvww
   %echo "${x#a*a}"
   bbaarealwwvvww
  
   其中 , # 表示掐頭, 由於鍵盤上 # 在 $ 的左面。
   其中 , % 表示%,  由於鍵盤上 % 在 $ 的右面。
   單個的表示最小匹配,雙個表示最大匹配。
   也就是說,當匹配的有多種方案的時候,選擇匹配的最大長度仍是最小長度。
 
6 字符串的替換
   %x=abcdabcd
   %echo ${x/a/b} # 只替換一個
   bbcdabcd
   %echo ${x//a/b} # 替換全部
   bbcdbbcd
   不可使用 regexp , 只能用 * ? 的文件擴展方式。
 
7 字符的處理
    %x=abcd
    printf "%30s\n" $x
    右邊補空格
    printf "%-30s\n" $x
    左邊補空格
 
8 字符串使用例子

字符有多是數字、字母、空格、其餘特殊字符,而字符串有多是它們任何一種或者多種的組合,在組合以後還可能造成一個具備特定意義的字符串,諸如郵件地址,URL地址等。php

概要示例: 下面咱們來看看如何判斷字符的類型。html

// 數字或者數字組合(可以返回結果,即程序退出狀態是0,說明屬於這種類型,反之否則)
$ i=5;j=9423483247234;
$ echo $i | grep [0-9]*
5
$ echo $j | grep [0-9]*
9423483247234
$ echo $j | grep [0-9]* >/dev/null
$ echo $?
0
// 字符組合(小寫字母、大寫字母、二者的組合)
$ c="A"; d="fwefewjuew"; e="fewfEFWefwefe"
$ echo $c | grep [A-Z]
A
$ echo $d | grep "[a-z]*"
fwefewjuew
$ echo $e | grep "[a-zA-Z]*"
fewfEFWefwefe
// 字母和數字的組合
$ ic="432fwfwefeFWEwefwef"
$ echo $ic | grep "[0-9a-zA-Z]*"
432fwfwefeFWEwefwef
// 空格或者Tab鍵等
$ echo " " | grep " "

$ echo -e "\t" | grep "[[:space:]]" #[[:space:]]會同時匹配空格和TAB鍵linux

$ echo -e " \t" | grep "[[:space:]]"正則表達式

$ echo -e "\t" | grep "<tab>" #<tab>爲在鍵盤上按下TAB鍵,而不是字符<tab>
// 匹配郵件地址
$ echo "test2007@lzu.cn" | grep "[0-9a-zA-Z\.]*@[0-9a-zA-Z\.]"
test2007@lzu.cn
// 匹配URL地址(以http連接爲例)
$ echo "http://news.lzu.edu.cn/article.jsp?newsid=10135" | grep "http://[0-9a-zA-Z\./=?]*"
http://news.lzu.edu.cn/article.jsp?newsid=10135shell

 


說明:
[1] /dev/null和/dev/zero是很是有趣的兩個設備,它們都猶如一個黑洞,什麼東西掉進去都會消失殆盡;後者則是一個能源箱,你總能從那裏取到0,直到你退出。二者的部分用法見:關於zero及NULL設備的一些問題
[2] [[:space:]]是grep用於匹配空格或者TAB鍵類型字符串的一種標記,其餘相似的標記請查看grep的幫助,man grep。
[3] 上面都是用grep來進行模式匹配,實際上sed, awk均可以用來作模式匹配,關於匹配中用到的正則匹配模式知識,你們能夠參考正則匹配模式,更多相關資料請看參考資料。
[4] 若是僅僅想判斷字符串是否爲空,即判斷字符串的長度是否爲零,那麼能夠簡單的經過test命令的-z選項來判斷,具體用法見test命令,man test.數據庫

概要示例: 判斷字符是否可打印?如何控制字符在終端的顯示。編程

// 用grep判斷某個字符是否爲可打印字符
$ echo "\t\n" | grep "[[:print:]]"
\t\n
$ echo $?
0
$ echo -e "\t\n" | grep "[[:print:]]"
$ echo $?
1
// 用echo的-e選項在屏幕控制字符顯示位置、顏色、背景等
$ echo -e "\33[31;40m" #設置前景色爲黑色,背景色爲紅色
$ echo -e "\33[11;29H Hello, World\!" #在屏幕的第11行,29列開始打印字符串Hello,World!
// 在屏幕的某個位置動態顯示當前系統時間
$ while :; do echo -e "\33[11;29H "$(date "+%Y-%m-%d %H:%M:%S"); done
// 用col命令過濾掉某些控制字符,在處理諸如script,screen等截屏命令的輸出結果時,頗有用
$ screen -L
$ cat /bin/cat
$ exit
$ cat screenlog.0 | col -b   # 把一些控制字符過濾後,就能夠保留可讀的操做日誌windows

 


更多關於字符在終端的顯示控制方法,請參考資料[20]和字符顯示實例[21]:用shell實現的一個動態時鐘。數組

1.2 字符串的長度瀏覽器

概要示例: 除了組成字符串的字符類型外,字符串還有哪些屬性呢?組成字符串的字符個數。下面咱們來計算字符串的長度,即全部字符的個數,並簡單介紹幾種求字符串中指定字符個數的方法。

// 計算某個字符串的長度,即全部字符的個數[這計算方法是五花八門,擇其優着而用之]
$ var="get the length of me"
$ echo ${var}     # 這裏等同於$var
get the length of me
$ echo ${#var}
20
$ expr length "$var"
20
$ echo $var | awk '{printf("%d\n", length($0));}'
20
$ echo -n $var | wc -c
20
// 計算某些指定一個字符或者多個字符的個數
$ echo $var | tr -cd g | wc -c
2
$ echo -n $var | sed -e 's/[^g]//g' | wc -c
2
$ echo -n $var | sed -e 's/[^gt]//g' | wc -c
5
// 若是要統計單詞個數,更多相關信息見《shell編程之數值計算》之 _單詞統計_ 實例。
$ echo $var | wc -w
5
$ echo "$var" | tr " " "\n" | grep get | uniq -c
1
$ echo "$var" | tr " " "\n" | grep get | wc -l
1

 


說明:
${}操做符在Bash裏頭一個「大牛」,能勝任至關多的工做,具體就看看網中人的《shell十三問》之《Shell十三問》之"$(( )) 與 $( ) 還有${ } 差在哪?" 吧。

1.3 字符串的存儲

在咱們看來,字符串是一連串的字符而已,可是爲了操做方便,咱們每每可讓字符串呈現出必定的結構。在這裏,咱們不關心字符串在內存中的實際存儲結構,僅僅關係它呈現出來的邏輯結構。好比,這樣一個字符串:"get the length of me",咱們能夠從不一樣的方面來呈現它。

1.3.1 經過字符在串中的位置來呈現它

這樣咱們就能夠經過指定位置來找到某個子串。這在c語言裏頭一般能夠利用指針來作。而在shell編程中,有不少可用的工具,諸如expr,awk都提供了相似的方法來實現子串的查詢動做。二者都幾乎支持模式匹配(match)和徹底匹配(index)。這在後面的字符串操做中將詳細介紹。

1.3.2 根據某個分割符來取得字符串的各個部分

這裏最多見的就是行分割符、空格或者TAB分割符了,前者用來當行號,咱們彷佛已經司空見慣了,由於咱們的編輯器就這樣「莫名」地處理着行分割符(在unix下爲\n,在其餘系統下有一些不一樣,好比windows下爲\r\n)。而空格或者TAB鍵常常用來分割數據庫的各個字段,這彷佛也是司空見慣的事情。

正是由於這樣,因此產生了大量優秀的行編輯工具,諸如grep,awk,sed等。在「行內」(姑且這麼說吧,就是處理單行,即字符串裏頭再也不包含行分割符)的字符串分割方面,cut和awk提供了很是優越的「行內」(處理單行)處理能力。

1.3.3 更方便地處理用分割符分割好的各個部分

一樣是用到分割符,但爲了更方便的操做分割之後的字符串的各個部分,咱們抽象了「數組」這麼一個數據結構,從而讓咱們更加方便地經過下標來獲取某個指定的部分。bash提供了這麼一種數據結構,而優秀的awk也一樣提供了它,咱們這裏將簡單介紹它們的用法。

概要示例:利用數組存放"get the length of me"的用空格分開的各個部分。

//1. bash提供的數組數據結構,它是以數字爲下標的,和C語言從0開始的下標同樣
$ var="get the length of me"
$ var_arr=($var)    #這裏把字符串var存放到字符串數組var_arr中了,默認以空格做爲分割符
$ echo ${var_arr[0]} ${var_arr[1]} ${var_arr[2]} ${var_arr[3]} ${var_arr[4]}
get the length of me
$ echo ${var_arr[@]}    #這個就是整個字符串全部部分啦,這裏能夠用*代替@,下同
get the length of me
$ echo ${#var_arr[@]}    #記得上面求某個字符串的長度麼,#操做符,若是想求某個數組元素的字符串長度,那麼就把@換成下標吧
5
// 你也能夠直接給某個數組元素賦值
$ var_arr[5]="new_element"
$ echo ${var_arr[5]}
6
$ echo ${var_arr[5]}
new_element
// bash裏頭實際上還提供了一種相似於「數組」的功能,即"for i in 用指定分割符分開的字符串" 的用法
// 即,你能夠很方便的獲取某個字符串的某個部分
$ for i in $var; do echo -n $i" "; done;
get the length of me

//2. awk裏頭的數組,注意比較它和bash提供的數組的異同
// split把一行按照空格分割,存放到數組var_arr中,並返回數組的長度。注意:這裏的第一個元素下標不是0,而是1
$ echo $var | awk '{printf("%d %s\n", split($0, var_arr, " "), var_arr[1]);}' 
5 get
// 實際上,上面的操做很相似awk自身的行處理功能:awk默認把一行按照空格分割爲多個域,並能夠經過$1,$2,$3...來獲取,$0表示整行
// 這裏的NF是該行的域的總數,相似於上面數組的長度,它一樣提供了一種經過「下標」訪問某個字符串的功能
$ echo $var | awk '{printf("%d | %s %s %s %s %s | %s\n", NF, $1, $2, $3, $4, $5, $0);}'
5 | get the length of me | get the length of me
// awk的「數組」功能何止於此呢,看看它的for引用吧,注意,這個和bash裏頭的for不太同樣,i不是元素自己,而是下標
$ echo $var | awk '{split($0, var_arr, " "); for(i in var_arr) printf("%s ",var_arr);}'
get the length of me 
$ echo $var | awk '{split($0, var_arr, " "); for(i in var_arr) printf("%s ",i);}'
1 2 3 4 5 
// awk還有更「厲害」的處理能力,它的下標能夠不是數字,而能夠是字符串,從而變成了「關聯」數組,這種「關聯」的做用在某些方便將讓咱們很是方便
// 好比,咱們這裏就實現一個非凡的應用,把某個文件中的某個系統調用名替換成地址,若是你真正用起它,你會感慨它的「鬼斧神工」的。
// 這就是我在一個場合最好才發現的隨好的實現方案:有興趣看看awk手冊帖子中我在3樓回覆的實例吧。
$ cat symbol 
sys_exit
sys_read
sys_close
$ ls /boot/System.map*
$ awk '{if(FILENAME ~ "System.map") map[$3]=$1; else {printf("%s\n", map[$1])}}' /boot/System.map-2.6.20-16-generic symbol 
c0129a80
c0177310
c0175d80
// 另外,awk還支持刪除某個數組元素,若是你不用了就能夠用delete函數給刪除掉。若是某些場合有須要的話,別忘了awk還支持二維數組。

 


okay,就介紹到這裏啦。爲何要介紹這些內容?再接着看下面的內容,你就會發現,那些有些的工具是怎麼產生和發展起來的了,若是累了,看看最後一篇參考資料吧,它介紹了一些linux命令名字的由來,說不定能夠幫助你理解本節下面的部分呢。

2. 字符串常規操做

字符串操做包括取子串、查詢子串、插入子串、刪除子串、子串替換、子串比較、子串排序、子串進制轉換、子串編碼轉換等。

2.1 取子串

概要示例:取子串的方法主要有:直接到指定位置求子串,字符匹配求子串。

// 按照位置取子串,好比從什麼位置開始,取多少個字符
$ var="get the length of me"
$ echo ${var:0:3}
get
$ echo ${var:(-2)}   # 方向相反呢
me
$ echo `expr substr "$var" 5 3` #記得把$var引發來,不然expr會由於空格而解析錯誤
the
$ echo $var | awk '{printf("%s\n", substr($0, 9, 6))}'
length

// 匹配字符求子串
$ echo ${var%% *} #從右邊開始計算,刪除最左邊的空格右邊的全部字符
get 
$ echo ${var% *} #從右邊開始計算,刪除第一個空格右邊的全部字符
get the length of
$ echo ${var##* } #從左邊開始計算,刪除最右邊的空格左邊的全部字符
me
$ echo ${var#* } #從左邊開始計算,刪除第一個空格左邊的全部字符
the length of me

$ echo $var | awk '{printf("%s\n", $1);}' # awk把$var按照空格分開爲多個變量,依次爲$1,$2,$3,$4,$5
get
$ echo $var | awk '{printf("%s\n", $5);}'
me

$ echo $var | cut -d" " -f 5 #差點把cut這個小東西忘記啦,用起來和awk相似, -d指定分割符,如同awk用-F指定分割符同樣,-f指定「域」,如同awk的$數字。

$ echo $var | sed 's/ [a-z]*//g' #刪除全部 空格+字母串 的字符串,因此get後面的所有被刪除了
get
$ echo $var | sed 's/[a-z]* //g'
me

$ echo $var | tr " " "\n" | sed -n 1p #sed有按地址(行)打印(p)的功能,記得先用tr把空格換成行號
get
$ echo $var | tr " " "\n" | sed -n 5p
me

// tr也能夠用來取子串哦,它也能夠相似#和%來「拿掉」一些字符串來實現取子串
$ echo $var | tr -d " "
getthelengthofme
$ echo $var | tr -cd "[a-z]" #把全部的空格都拿掉了,僅僅保留字母字符串,注意-c和-d的用法
getthelengthofme

 


說明:
[1] %和#的區別是,刪除字符的方向不同,前者在右,後者在左,%%和%,##和#的方向是前者是最大匹配,後者是最小匹配。(好的記憶方法見網中人的鍵盤記憶法:#$%是鍵盤依次從左到右的三個鍵)
[2] tr的-c選項是complement的縮寫,即invert,而-d選項是刪除的意思,tr -cd "[a-z]"這樣一來就變成保留全部的字母啦。

對於字符串的截取,實際上還有一些命令,若是head,tail等能夠實現有意思的功能,能夠截取某個字符串的前面、後面指定的行數或者字節數。例如:

$ echo "abcdefghijk" | head -c 4
abcd
$ echo -n "abcdefghijk" | tail -c 4
hijk

 


2.2. 查詢子串

概要示例:子串查詢包括:返回符合某個模式的子串自己和返回子串在目標串中的位置。

準備:在進行下面的操做以前,請把http://oss.lzu.edu.cn/blog/blog.php?do_showone/tid_1385.html連接中的內容複製到一個文本text裏頭,用於下面的操做。

// 查詢子串在目標串中的位置
$ var="get the length of me"
$ expr index "$var" t        #貌似僅僅能夠返回某個字符或者多個字符中第一個字符出現的位置

$ echo $var | awk '{printf("%d\n", match($0,"the"));}'    #awk卻能找出字串,match還能夠匹配正則表達式
5

// 查詢子串,返回包含子串的行(awk,sed均可以實現這些功能,可是grep最擅長)
$ grep "consists of" text   # 查詢text文件包含consists of的行,並打印這些行
$ grep "consists[[:space:]]of" -n -H text # 打印文件名,子串所在行的行號和該行的內容
$ grep "consists[[:space:]]of" -n -o text # 僅僅打印行號和匹配到的子串自己的內容
$ awk '/consists of/{ printf("%s:%d:%s\n",FILENAME, FNR, $0)}' text #看到沒?和grep的結果同樣
$ sed -n -e '/consists of/=;/consists of/p' text #一樣能夠打印行號

 


說明:
[1] awk,grep,sed都能經過模式匹配查找指定的字符串,可是它們各有擅長的領域,咱們將在後續的章節中繼續使用和比較它們,從而發現各自的優勢。
[2] 在這裏咱們姑且把文件內容當成了一個大的字符串,在後面的章節中咱們將專門介紹文件的操做,因此對文件內容中存放字符串的操做將會有更深刻的分析和介紹。

2.3. 子串替換

子串替換就是把某個指定的子串替換成其餘的字符串,實際上這裏就蘊含了「插入子串」和「刪除子串」的操做。例如,你想插入某個字符串到某個子串以前,就能夠把原來的子串替換成」子串+新的字符串「,若是想刪除某個子串,就把子串替換成空串。不過有些工具提供了一些專門的用法來作插入子串和刪除子串的操做,因此呆夥仍是會專門介紹的。另外,要想替換掉某個子串,通常都是先找到子串(查詢子串),而後再把它替換掉的,實質上不少工具在使用和設計上都體現了這麼一點。

概要示例:下面咱們把變量var中的空格替換成下劃線看看。

// 用{}運算符,還記得麼?網中人的教程。
$ var="get the length of me"
$ echo ${var/ /_}        #把第一個空格替換成下劃線
get_the length of me
$ echo ${var// /_}        #把全部空格都替換成了下劃線了
get_the_length_of_me

// 用awk,awk提供了轉換的最小替換函數sub和全局替換函數gsub,相似/和//
$ echo $var | awk '{sub(" ", "_", $0); printf("%s\n", $0);}'
get_the length of me
$ echo $var | awk '{gsub(" ", "_", $0); printf("%s\n", $0);}'
get_the_length_of_me

// 用sed了,子串替換但是sed的特長
$ echo $var | sed -e 's/ /_/'    #s <= substitude
get_the length of me
$ echo $var | sed -e 's/ /_/g'    #看到沒有,簡短兩個命令就實現了最小匹配和最大匹配g <= global
get_the_length_of_me

// 有忘記tr命令麼?能夠用替換單個字符的
$ echo $var | tr " " "_"
get_the_length_of_me
$ echo $var | tr '[a-z]' '[A-Z]'   #這個可有意思了,把全部小寫字母都替換爲大寫字母
GET THE LENGTH OF ME

 


說明:sed還有頗有趣的標籤用法呢,下面再介紹吧。

有一種比較有意思的字符串替換是,整個文件行的倒置,這個能夠經過tac命令實現,它會把文件中全部的行所有倒轉過來。在必定意義上來講,排序實際上也是一個字符串替換。

2.4. 插入子串

插入子串:就是在指定的位置插入子串,這個位置多是某個子串的位置,也多是從某個文件開頭算起的某個長度。經過上面的練習,咱們發現這二者之間其實是相似的。

公式:插入子串=把"old子串"替換成"old子串+new子串"或者"new子串+old子串"

概要示例::下面在var字符串的空格以前或以後插入一個下劃線

// 用{}
$ var="get the length of me"
$ echo ${var/ /_ }        #在指定字符串以前插入一個字符串
get_ the length of me
$ echo ${var// /_ }
get_ the_ length_ of_ me
$ echo ${var/ / _}        #在指定字符串以後插入一個字符串
get _the length of me
$ echo ${var// / _}
get _the _length _of _me

// 其餘的還用演示麼?這裏主要介紹sed怎麼用來插入字符吧,由於它的標籤功能頗有趣
$ echo $var | sed -e 's/\( \)/_\1/' #\(和\)將不匹配到的字符串存放爲一個標籤,按匹配順序爲\1,\2...
get_ the length of me
$ echo $var | sed -e 's/\( \)/_\1/g'
get_ the_ length_ of_ me
$ echo $var | sed -e 's/\( \)/\1_/'
get _the length of me
$ echo $var | sed -e 's/\( \)/\1_/g'
get _the _length _of _me

// 看看sed的標籤的順序是否是\1,\2....,看到沒?\2和\1掉換位置後,the和get的位置掉換了
$ echo $var | sed -e 's/\([a-z]*\) \([a-z]*\) /\2 \1 /g'
the get of length me
// sed還有專門的插入指令,a和i,分別表示在匹配的行後和行前插入指定字符
$ echo $var | sed '/get/a test'
get the length of me
test
$ echo $var | sed '/get/i test'
test
get the length of me

 


2.5. 刪除子串

刪除子串:應該很簡單了吧,把子串替換成「空」(什麼都沒有)不就變成了刪除麼。仍是來簡單複習一下替換吧。

概要示例::把var字符串中全部的空格給刪除掉。

鼓勵: 這樣一替換不知道變成什麼單詞啦,誰認得呢?可是中文倒是連在一塊兒的,因此中文有多難,你想到了麼?原來你也是個語言天才,而英語並不可怕,你有學會它的天賦,只要你有這個打算。

// 再用{}
$ echo ${var// /} 
getthelengthofme
// 再用awk
$ echo $var | awk '{gsub(" ","",$0); printf("%s\n", $0);}'
// 再用sed
$ echo $var | sed 's/ //g'
getthelengthofme
// 還有更簡單的tr命令,tr也能夠把" "給刪除掉,看
$ echo $var | tr -d " "
getthelengthofme

 


若是要刪除掉第一個空格後面全部的字符串該怎麼辦呢?還記得{}的#和%用法麼?若是不記得,回到這一節的還頭開始複習吧。(實際上刪除子串和取子串何嘗不是兩種互補的運算呢,刪除掉某些不想要的子串,也就同時取得另外那些想要的子串——這個世界就是一個「二元」的世界,很是有趣)

2.6. 子串比較

這個很簡單:還記得test命令的用法麼?man test。它能夠用來判斷兩個字符串是否相等的。另外,你發現了「字符串是否相等」和「字符串可否跟另一個字符串匹配"兩個問題之間的關係嗎?若是兩個字符串徹底匹配,那麼這兩個字符串就相等了。因此呢,上面用到的字符串匹配方法,也一樣能夠用到這裏。

2.7. 子串排序

差點忘記這個重要的內容了,子串排序但是常常用到的,常見的有按字母序、數字序等正序或反序排列。sort命令能夠用來作這個工做,它和其餘行處理命令同樣,是按行操做的,另外,它相似cut和awk,能夠指定分割符,並指定須要排序的列。

$ var="get the length of me"
$ echo $var | tr ' ' '\n' | sort   #正序排
get
length
me
of
the
$ echo $var | tr ' ' '\n' | sort -r #反序排
the
of
me
length
get
$ cat data.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
41 45 44 44 26 44 42 20 20 38 37 25 45 45 45
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
44 20 30 39 35 38 38 28 25 30 36 20 24 32 33
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
41 33 51 39 20 20 44 37 38 39 42 40 37 50 50
46 47 48 49 50 51 52 53 54 55 56
42 43 41 42 45 42 19 39 75 17 17
$ cat data.txt | sort -k 2 -n
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
44 20 30 39 35 38 38 28 25 30 36 20 24 32 33
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
41 33 51 39 20 20 44 37 38 39 42 40 37 50 50
42 43 41 42 45 42 19 39 75 17 17
41 45 44 44 26 44 42 20 20 38 37 25 45 45 45
46 47 48 49 50 51 52 53 54 55 56

 


2.7. 子串進制轉換

若是字母和數字字符用來計數,那麼就存在進制轉換的問題。在數值計算一節的回覆資料裏,咱們已經介紹了bc命令,這裏再簡單的複習一下。

$ echo "ibase=10;obase=16;10" | bc
A

 


說明:ibase指定輸入進制,obase指出輸出進制,這樣經過調整ibase和obase,你想怎麼轉就怎麼轉啦!

2.7. 子串編碼轉換

什麼是字符編碼?這個就不用介紹了吧,看過那些亂七八糟顯示的網頁麼?大可能是由於瀏覽器顯示時的」編碼「和網頁實際採用的」編碼「不一致致使的。字符編碼一般是指把一序列」可打印「字符轉換成二進制表示,而字符解碼呢則是執行相反的過程,若是這兩個過程不匹配,則出現了所謂的」亂碼「。

爲了解決」亂碼「問題呢?就須要進行編碼轉換。在linux下,咱們可使用iconv這個工具來進行相關操做。這樣的狀況常常在不一樣的操做系統之間移動文件,不一樣的編輯器之間交換文件的時候遇到,目前在windows下經常使用的漢字編碼是gb2312,而在linux下則大多采用utf8。

$ nihao_gb2312=$(echo "你好" | iconv -f utf8 -t gb2312)
$ echo $nihao_gb2312
? ? ? ? 
$ nihao_utf8=$(echo $nihao_gb2312 | iconv -f gb2312 -t utf8)
$ PS1="$ "
$ echo $nihao_utf8
你好

 


說明:個人終端默認編碼是utf8,因此結果如上。

3. 字符串操做範例

實際上,在用Bash編程時,大部分時間都是在處理字符串,所以把這一節熟練掌握很是重要。

3.1 處理一個很是有意義的字符串:URL地址

範例演示:處理URL地址

URL地址(URL(Uniform Resoure Locator:統一資源定位器)是WWW頁的地址)幾乎是咱們平常生活的玩伴,咱們已經到了沒法離開它的地步啦,對它的操做不少,包括判斷URL地址的有效性,截取地址的各個部分(服務器類型、服務器地址、端口、路徑等)並對各個部分進行進一步的操做。

下面咱們來具體處理這個URL地址:
ftp://anonymous:ftp@mirror.lzu.edu.cn/software/scim-1.4.7.tar.gz

$ url="ftp://anonymous:ftp@mirror.lzu.edu.cn/software/scim-1.4.7.tar.gz"
// 匹配URL地址,判斷URL地址的有效性
$ echo $url | grep "ftp://[a-z]*:[a-z]*@[a-z\./-]*"
// 截取服務器類型
$ echo ${url%%:*}
ftp
$ echo $url | cut -d":" -f 1
ftp
// 截取域名
$ tmp=${url##*@} ; echo ${tmp%%/*}
mirror.lzu.edu.cn
// 截取路徑
$ tmp=${url##*@} ; echo ${tmp%/*}
mirror.lzu.edu.cn/software
// 截取文件名
$ basename $url
scim-1.4.7.tar.gz
$ echo ${url##*/}
scim-1.4.7.tar.gz
// 截取文件類型(擴展名)
$ echo $url | sed -e 's/.*[0-9].\(.*\)/\1/g'
tar.gz

 


有了上面的知識,咱們就能夠很是容易地進行這些工做啦:修改某個文件的文件名,好比調整它的編碼,下載某個網頁裏頭的全部pdf文檔等。這些就做爲練習本身作吧,若是遇到問題,能夠在回帖交流。相應地能夠參考這個例子:

[1] 用腳本下載某個網頁中的英文原著(pdf文檔)
http://oss.lzu.edu.cn/blog/blog.php?do_showone/tid_1228.html

3.2 處理格式化的文本:/etc/passwd

平時作工做,大多數時候處理的都是一些「格式化」的文本,好比相似/etc/passwd這樣的有固定行和列的文本,也有相似tree命令輸出的那種具備樹形結構的文本,固然還有其餘具備特定結構的文本。

關於樹狀結構的文本的處理,能夠考慮看看這兩個例子:
[1] 用AWK轉換樹形數據成關係表
http://oss.lzu.edu.cn/blog/blog.php?do_showone/tid_1260.html
[2] 用Graphviz進行可視化操做──繪製函數調用關係圖
http://oss.lzu.edu.cn/blog/blog.php?do_showone/tid_1425.html
實際上,只要把握好特性結構的一些特色,並根據具體的應用場合,處理起來就不會困難。

下面咱們來介紹具體有固定行和列的文本的操做,以/etc/passwd文件爲例。關於這個文件的幫忙和用戶,請經過man 5 passwd查看。下面咱們對這個文件以及相關的文件進行一些有意義的操做。

// 選取/etc/passwd文件中的用戶名和組ID兩列
$ cat /etc/passwd | cut -d":" -f1,4
// 選取/etc/group文件中的組名和組ID兩列
$ cat /etc/group | cut -d":" -f1,3
// 若是想找出全部用戶所在的組,怎麼辦?
$ join -o 1.1,2.1 -t":" -1 4 -2 3 /etc/passwd /etc/group
root:root
bin:bin
daemon:daemon
adm:adm
lp:lp
pop:pop
nobody:nogroup
falcon:users
// 先解釋一下:join命令用來鏈接兩個文件,有點相似於數據庫的兩個表的鏈接。-t指定分割符,"-1 4 -2 3"指定按照第一個文件的第4列和第二個文件的第3列,即組ID進行鏈接,"-o 1.1,2.1"表示僅僅輸出第一個文件的第一列和第二個文件的第一列,這樣就獲得了咱們要的結果,不過,惋惜的是,這個結果並不許確,再進行下面的操做,你就會發現:
$ cat /etc/passwd | sort -t":" -n -k 4 > /tmp/passwd
$ cat /etc/group | sort -t":" -n -k 3 > /tmp/group
$ join -o 1.1,2.1 -t":" -1 4 -2 3 /tmp/passwd /tmp/group
halt:root
operator:root
root:root
shutdown:root
sync:root
bin:bin
daemon:daemon
adm:adm
lp:lp
pop:pop
nobody:nogroup
falcon:users
games:users
// 能夠看到這個結果纔是正確的,因此之後使用join千萬要注意這個問題,不然採起更保守的作法彷佛更能保證正確性,更多關於文件鏈接的討論見參考資料[14]

 


上面涉及到了處理某格式化行中的指定列,包括截取(如SQL的select用法),鏈接(如SQL的join用法),排序(如SQL的order by用法),均可以經過指定分割符來拆分某個格式化的行,另外,「截取」的作法還有不少,不光是cut,awk,甚至經過IFS指定分割符的read命令也能夠作到,例如:
$ IFS=":"; cat /etc/group | while read C1 C2 C3 C4; do echo $C1 $C3; done

 

所以,熟悉這些用法,咱們的工做將變得很是靈活有趣。

到這裏,須要作一個簡單的練習,如何把按照列對應的用戶名和用戶ID轉換成按照行對應的,即把相似下面的數據:
$ cat /etc/passwd | cut -d":" -f1,3 --output-delimiter=" "
root 0
bin 1
daemon 2

 

轉換成:
$ cat a
root    bin     daemon 
0       1       2

 

並轉換回去,有什麼辦法呢?記得諸如tr,paste,split等命令均可以使用。

相關文章
相關標籤/搜索