shell 指令碼的學習 IFS解惑

來源:互聯網
上載者:User

標籤:style   blog   http   color   使用   io   strong   for   

一、IFS 介紹

     Shell 指令碼中有個變數叫 IFS(Internal Field Seprator) ,內部域分隔字元。完整定義是The shell uses the value stored in IFS, which is the space, tab, and newline characters by default, to delimit words for the read and set commands, when parsing output from command substitution, and when performing variable substitution.

     Shell 的環境變數分為 set, env 兩種,其中 set 變數可以通過 export 工具匯入到 env 變數中。其中,set 是顯示設定shell變數,僅在本 shell 中有效;env 是顯示設定使用者環境變數 ,僅在當前會話中有效。換句話說,set 變數裡包含了 env 變數,但 set 變數不一定都是 env 變數。這兩種變數不同之處在於變數的範圍不同。顯然,env 變數的範圍要大些,它可以在 subshell 中使用。

     而 IFS 是一種 set 變數,當 shell 處理"命令替換"和"參數替換"時,shell 根據 IFS 的值,預設是 space, tab, newline 來拆解讀入的變數,然後對特殊字元進行處理,最後重新組合賦值給該變數。

二、IFS 簡單一實例

1、查看變數 IFS 的值。

[plain] view plaincopyprint?
  1. $ echo $IFS  
  2.   
  3. $ echo "$IFS" | od -b  
  4. 0000000 040 011 012 012  
  5. 0000004  
$ echo $IFS$ echo "$IFS" | od -b0000000 040 011 012 0120000004

直接輸出IFS是看不到的,把它轉化為二進位就可以看到了,"040"是空格,"011"是Tab,"012"是分行符號"\n" 。最後一個 012 是因為 echo 預設是會換行的。

2、$* 和 [email protected] 的細微差別
     從下面的例子中可以看出,如果是用冒號引起來,表示這個變數不用IFS替換!!所以可以看到這個變數的"原始值"。反之,如果不加引號,輸出時會根據IFS的值來分割後合并輸出! $* 是按照IFS中的第一個值來確定的!下面這兩個例子還有細微的差別!

[plain] view plaincopyprint?
  1. $ IFS=:;  
  2. $ set x y z  
  3. $ echo $*  
  4. x y z  
  5. $ echo "$*"  
  6. x:y:z  
  7. $ echo [email protected]  
  8. x y z  
  9. $ echo "[email protected]"  
  10. x y z  
$ IFS=:;$ set x y z$ echo $*x y z$ echo "$*"x:y:z$ echo [email protected]x y z$ echo "[email protected]"x y z

 上例 set 變數其實是3個參數,而下面這個例子實質是2個參數,即 set "x y z"  和 set x y z 是完全不同的。

[plain] view plaincopyprint?
  1. $ set "x" "y z"  
  2. $ echo $*  
  3. x y z  
  4. $ echo "$*"  
  5. x:y z  
  6. $ echo [email protected]  
  7. x y z  
  8. $ echo "[email protected]"  
  9. x y z  
  10. $ echo $* |od -b  
  11. 0000000 170 040 171 040 172 012  
  12. 0000006  
  13. $ echo "$*" |od -b  
  14. 0000000 170 072 171 040 172 012  
  15. 0000006  
$ set "x" "y z"$ echo $*x y z$ echo "$*"x:y z$ echo [email protected]x y z$ echo "[email protected]"x y z$ echo $* |od -b0000000 170 040 171 040 172 0120000006$ echo "$*" |od -b0000000 170 072 171 040 172 0120000006

小結:$* 會根據 IFS 的不同來組合值,而 [email protected] 則會將值用" "來組合值!

3、for 迴圈中的奇怪現象

[plain] view plaincopyprint?
  1. $ for x in $var ;do echo $x |od -b ;done  
  2. 0000000 012  
  3. 0000001  
  4. 0000000 040 141 012  
  5. 0000003  
  6. 0000000 142 012  
  7. 0000002  
  8. 0000000 012  
  9. 0000001  
  10. 0000000 143 012  
  11. 0000002  
$ for x in $var ;do echo $x |od -b ;done0000000 01200000010000000 040 141 01200000030000000 142 01200000020000000 01200000010000000 143 0120000002

 

先暫且不解釋 for 迴圈的內容!看下面這個輸出!IFS 的值同上! var=": a:b::c:"

[plain] view plaincopyprint?
  1. $ echo $var |od -b  
  2. 0000000 040 040 141 040 142 040 040 143 012  
  3. 0000011  
  4. $ echo "$var" |od -b  
  5. 0000000 072 040 141 072 142 072 072 143 072 012  
  6. 0000012  
$ echo $var |od -b0000000 040 040 141 040 142 040 040 143 0120000011$ echo "$var" |od -b0000000 072 040 141 072 142 072 072 143 072 0120000012

"$var"的值應該沒做替換,所以還是 ": a:b::c:" (注 "072" 表示冒號),但是$var 則發生了變化!注意輸出的最後一個冒號沒有了,也沒有替換為空白格!Why?

 

使用 $var 時是經曆了這樣一個過程!首先,按照這樣的規則 [變數][IFS][變數][IFS]……根據原始 var 值中所有的分割符(此處是":")劃分出變數,如果IFS的值是有多個字元組成,如IFS=":;",那麼此處的[IFS]指的是IFS中的任意一個字元($* 是按第一個字元來分隔!),如 ":" 或者 ";" ,後面不再對[IFS]做類似說明!(註:[IFS]會有多個值,多虧 #blackold 的提醒);然後,得到類似這樣的 list, ""   " a"   "b"  ""   "c"  。如果此時 echo $var,則需要在這些變數之間用空格隔開,也就是""  [space]   "  a"  [space]  "b" [space]  "" [space]  "c" ,忽略掉空值,最終輸出是 [space][space]a[space]b[space][space]c

如果最後一個字元不是分隔字元,如 var="a:b",那麼最後一個分隔字元後的變數就是最後一個變數!

這個地方要注意下!!如果IFS就是空格,那麼類似於" [space][space]a[space]b[space][space]c "會合并重複的部分,且去頭空格,去尾空格,那麼最終輸出會變成類似 a[space]b[space]c ,所以,如果IFS是預設值,那麼處理的結果就很好算出來,直接合并、忽略多餘空格即可!

另外,$* 和 [email protected] 在函數中的處理過程是這樣的(只考慮"原始值"!)!"[email protected]",就是像上面處理後賦值,但是 "$*" 卻不一樣!它的值是用分隔字元(如":")而不是空格隔開!具體例子見最後一個例子!

好了,現在來解釋 for 迴圈的內容。for 迴圈遍曆上面這個列表就可以了,所以 for 迴圈的第一個輸出是空!("012"是echo輸出的分行符號 )。。。。後面的依次類推!不信可以試試下面這個例子,結果是一樣的!

[plain] view plaincopyprint?
  1. $ for x in "" " a" "b" "" "c" ;do echo $x |od -b ;done  
  2. 0000000 012  
  3. 0000001  
  4. 0000000 040 141 012  
  5. 0000003  
  6. 0000000 012  
  7. 0000001  
  8. 0000000 142 012  
  9. 0000002  
  10. 0000000 012  
  11. 0000001  
  12. 0000000 143 012  
  13. 0000002  
$ for x in "" " a" "b" "" "c" ;do echo $x |od -b ;done0000000 01200000010000000 040 141 01200000030000000 01200000010000000 142 01200000020000000 01200000010000000 143 0120000002

三、IFS的其他執行個體

Example 1:

 

[plain] view plaincopyprint?
  1. $ IFS=:  
  2. $ var=ab::cd  
  3. $ echo $var  
  4. ab  cd  
  5. $ echo "$var"  
  6. ab::cd  
$ IFS=:$ var=ab::cd$ echo $varab  cd$ echo "$var"ab::cd

解釋下:x 的值是 "ab::cd",當進行到 echo $x 時,因為$符,所以會進行變數替換。Shell 根據 IFS 的值將 x 分解為 ab "" cd,然後echo,插入空隔,ab[space]""[space]cd,忽略"",輸出  ab  cd 。

Example 2 :

[plain] view plaincopyprint?
  1. $ read a  
  2.        xy  z  
  3. $ echo $a  
  4. xy  z  
$ read a       xy  z$ echo $axy  z

解釋:這是 http://bbs.chinaunix.net/thread-207178-1-1.html 上的一個例子。此時IFS是預設值,本希望把所有的輸入(包括空格)都放入變數a中,但是輸出的a卻把前面的空格給忽略了!!原因是:預設的 IFS 會按 space tab newline 來分割。這裡需要注意的一點是,read 命令的實現過程,即在讀入時已經替換了。解決辦法是在開頭加上一句 IFS=";" ,這裡必須加上雙引號,因為分號有特殊含義。

Example 3 :

[plain] view plaincopyprint?
  1. $ tmp="   xy z"  
  2. $ a=$tmp  
  3. $ echo $a  
  4. $ echo "$a"  
$ tmp="   xy z"$ a=$tmp$ echo $a$ echo "$a"

解釋:什麼時候會根據 IFS 來"處理"呢?我覺得是,對於不加引號的變數,使用時都會參考IFS,但是要注意其原始值!

Example 4 :

[plain] view plaincopyprint?
  1. #!/bin/bash  
  2. IFS_old=$IFS      #將原IFS值儲存,以便用完後恢複  
  3. IFS=$’\n’        #更改IFS值為$’\n’ ,注意,以斷行符號做為分隔字元,IFS必須為:$’\n’  
  4. for i in $((cat pwd.txt)) #pwd.txt 來自這個命令:cat /etc/passwd >pwd.txt  
  5. do  
  6.     echo $i  
  7. done  
  8. IFS=$IFS_old      #恢複原IFS值  
#!/bin/bashIFS_old=$IFS      #將原IFS值儲存,以便用完後恢複IFS=$’\n’        #更改IFS值為$’\n’ ,注意,以斷行符號做為分隔字元,IFS必須為:$’\n’for i in $((cat pwd.txt)) #pwd.txt 來自這個命令:cat /etc/passwd >pwd.txtdo    echo $idoneIFS=$IFS_old      #恢複原IFS值

另外一個例子,把IP地址逆轉輸出:

Example 5 :

[plain] view plaincopyprint?
  1. #!/bin/bash  
  2.   
  3. IP=220.112.253.111  
  4. IFS="."  
  5. TMPIP=$(echo $IP)  
  6. IFS=" " # space  
  7. echo $TMPIP  
  8. for x in $TMPIP ;do   
  9.     Xip="${x}.$Xip"  
  10. done  
  11. echo ${Xip%.}  
#!/bin/bashIP=220.112.253.111IFS="."TMPIP=$(echo $IP)IFS=" " # spaceecho $TMPIPfor x in $TMPIP ;do     Xip="${x}.$Xip"doneecho ${Xip%.}

Complex_Example 1:  http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=3660898&page=1#pid21798049

[plain] view plaincopyprint?
  1. function output_args_ifs(){  
  2.     echo "=$*"  
  3.     echo "="$*  
  4.     for m in $* ;do   
  5.         echo "[$m]"  
  6.     done  
  7. }  
  8.   
  9. IFS=‘:‘  
  10. var=‘::a:b::c:::‘  
  11. output_args_ifs $var  
function output_args_ifs(){    echo "=$*"    echo "="$*    for m in $* ;do         echo "[$m]"    done}IFS=‘:‘var=‘::a:b::c:::‘output_args_ifs $var

 

輸出為:

[plain] view plaincopyprint?
  1. =::a:b::c::  # 少了最後一個冒號!看前面就知道為什麼了  
  2. =  a b  c   
  3. []  
  4. []  
  5. [a]  
  6. [b]  
  7. []  
  8. [c]  
  9. []  
=::a:b::c::  # 少了最後一個冒號!看前面就知道為什麼了=  a b  c [][][a][b][][c][]

由於 "output_args_ifs $var" 中 $var 沒有加引號,所以根據IFS替換!根據IFS劃分出變數: ""  ""  "a"  "b"  ""  "c" "" ""(可以通過輸出 $# 來測試參數的個數!),重組的結果為

 "[email protected]" 的值是  "" [space] "" [space]  "a" [space]  "b"  [space] "" [space]  "c" [space] "" [space] "",可以通過,echo==>"  a b  c   "
"$*" 的值是   "" [IFS] "" [IFS]  "a" [IFS]  "b"  [IFS] "" [IFS]  "c" [IFS] "" [IFS] "",忽略"",echo=>"::a:b::c::"

注意, $* 和 [email protected] 的值都是  ""   ""   "a"   "b"   ""   "c"  ""  "" 。可以說是一個列表……因為他們本來就是由 $1 $2 $3……組成的。

所以,《Linux程式設計》裡推薦使用 [email protected],而不是$*

http://blog.csdn.net/whuslei/article/details/7187639

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.