IFS分隔符
1 IFS 介紹
??Shell 腳本中有個(gè)變量叫 IFS(Internal Field Seprator) 期丰,內(nèi)部域分隔符短曾。完整定義是
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 的環(huán)境變量分為 set, env 兩種,其中 set 變量可以通過 export 工具導(dǎo)入到 env 變量中。
??其中,set 是顯示設(shè)置shell變量岸更,僅在本 shell 中有效潜圃;
??env 是顯示設(shè)置用戶環(huán)境變量 贩据,僅在當(dāng)前會(huì)話中有效荣病。
??換句話說,set 變量里包含了 env 變量渗柿,但 set 變量不一定都是 env 變量个盆。這兩種變量不同之處在于變量的作用域不同。顯然朵栖,env 變量的作用域要大些颊亮,它可以在 subshell 中使用。
??而 IFS 是一種 set 變量陨溅,當(dāng) shell 處理"命令替換"和"參數(shù)替換"時(shí)终惑,shell 根據(jù) IFS 的值,默認(rèn)是 space, tab, newline 來拆解讀入的變量门扇,然后對(duì)特殊字符進(jìn)行處理雹有,最后重新組合賦值給該變量。
2 IFS簡(jiǎn)單實(shí)例
2.1 查看變量 IFS 的值
$ echo $IFS
$ echo "$IFS" | od -b
0000000 040 011 012 012
0000004
??直接輸出IFS是看不到的臼寄,把它轉(zhuǎn)化為二進(jìn)制就可以看到了霸奕,"040"是空格,"011"是Tab吉拳,"012"是換行符"\n" 质帅。最后一個(gè) 012 是因?yàn)?echo 默認(rèn)是會(huì)換行的。
2.2
@ 的細(xì)微差別
??如果是用冒號(hào)引起來留攒,表示這個(gè)變量不用IFS替換煤惩!所以可以看到這個(gè)變量的"原始值"。反之炼邀,如果不加引號(hào)魄揉,輸出時(shí)會(huì)根據(jù)IFS的值來分割后合并輸出
$ IFS=:;
$ set x y z
$ echo $*
x y z
$ echo "$*"
x:y:z
$ echo $@
x y z
$ echo "$@"
x y z
??上例 set 變量其實(shí)是3個(gè)參數(shù),而下面這個(gè)例子實(shí)質(zhì)是2個(gè)參數(shù)拭宁,即 set "x y z" 和 set x y z 是完全不同的
$ set "x" "y z"
$ echo $*
x y z
$ echo "$*"
x:y z
$ echo $@
x y z
$ echo "$@"
x y z
$ echo $* | od -b
0000000 170 040 171 040 172 012
0000006
$ echo "$*" | od -b
0000000 170 072 171 040 172 012
0000006
??$* 會(huì)根據(jù) IFS 的不同來組合值什猖,而 $@ 則會(huì)將值用" "來組合值
2.3 for 循環(huán)中IFS的奇怪現(xiàn)象
$ for x in $var ;do echo $x | od -b ;done
0000000 012
0000001
0000000 040 141 012
0000003
0000000 142 012
0000002
0000000 012
0000001
0000000 143 012
0000002
??且不解釋 for 循環(huán)的內(nèi)容!看下面這個(gè)輸出红淡!IFS 的值同上不狮! var=": a:b::c:"
$ echo $var |od -b
0000000 040 040 141 040 142 040 040 143 012
0000011
$ echo "$var" |od -b
0000000 072 040 141 072 142 072 072 143 072 012
0000012
??"$var"的值沒做替換,所以還是 ": a:b::c:" (注 "072" 表示冒號(hào))在旱,但是$var 則發(fā)生了變化摇零!注意輸出的最后一個(gè)冒號(hào)沒有了,也沒有替換為空格
??使用 $var 時(shí)是經(jīng)歷了這樣一個(gè)過程桶蝎!首先驻仅,按照這樣的規(guī)則 [變量][IFS][變量][IFS]……根據(jù)原始 var 值中所有的分割符(此處是":")劃分出變量谅畅,如果IFS的值是有多個(gè)字符組成,如IFS=":;"噪服,那么此處的[IFS]指的是IFS中的任意一個(gè)字符(var贩挣,則需要在這些變量之間用空格隔開,也就是"" [space] " a" [space] "b" [space] "" [space] "c" 没酣,忽略掉空值王财,最終輸出是 [space][space]a[space]b[space][space]c !
??如果最后一個(gè)字符不是分隔符裕便,如 var="a:b"搪搏,那么最后一個(gè)分隔符后的變量就是最后一個(gè)變量!
??如果IFS就是空格闪金,那么類似于" [space][space]a[space]b[space][space]c "會(huì)合并重復(fù)的部分疯溺,且去頭空格,去尾空格哎垦,那么最終輸出會(huì)變成類似 a[space]b[space]c 囱嫩,所以,如果IFS是默認(rèn)值漏设,那么處理的結(jié)果就很好算出來墨闲,直接合并、忽略多余空格即可郑口!
??$* 和 $@ 在函數(shù)中的處理過程是這樣的(只考慮"原始值"鸳碧!)!"$@"犬性,就是像上面處理后賦值瞻离,但是 "$*" 卻不一樣!它的值是用分隔符(如":")而不是空格隔開乒裆!具體例子見最后一個(gè)例子套利!
??現(xiàn)在來解釋 for 循環(huán)的內(nèi)容。for 循環(huán)遍歷上面這個(gè)列表就可以了,所以 for 循環(huán)的第一個(gè)輸出是空肉迫!("012"是echo輸出的換行符 )验辞。。喊衫。跌造。后面的依次類推!
??下面這個(gè)例子族购,結(jié)果是一樣
$ for x in "" " a" "b" "" "c" ;do echo $x |od -b ;done
0000000 012
0000001
0000000 040 141 012
0000003
0000000 012
0000001
0000000 142 012
0000002
0000000 012
0000001
0000000 143 012
0000002
3 IFS的其他實(shí)例
3.1 Example 1:
$ IFS=:
$ var=ab::cd
$ echo $var
ab cd
$ echo "$var"
ab::cd
??解釋:x 的值是 "ab::cd"壳贪,當(dāng)進(jìn)行到 echo $x 時(shí),因?yàn)?符联四,所以會(huì)進(jìn)行變量替換撑碴。Shell 根據(jù) IFS 的值將 x 分解為 ab "" cd撑教,然后echo朝墩,插入空隔,ab[space]""[space]cd伟姐,忽略""收苏,輸出 ab cd 。
3.2 Example 2 :
$ read a
xy z
$ echo $a
xy z
??解釋:這是 http://bbs.chinaunix.net/thread-207178-1-1.html 上的一個(gè)例子愤兵。此時(shí)IFS是默認(rèn)值鹿霸,本希望把所有的輸入(包括空格)都放入變量a中,但是輸出的a卻把前面的空格給忽略了秆乳!
??原因是:默認(rèn)的 IFS 會(huì)按 space tab newline 來分割懦鼠。這里需要注意的一點(diǎn)是,read 命令的實(shí)現(xiàn)過程屹堰,即在讀入時(shí)已經(jīng)替換了肛冶。解決辦法是在開頭加上一句 IFS=";" ,這里必須加上雙引號(hào)扯键,因?yàn)榉痔?hào)有特殊含義睦袖。
3.3 Example 3 :
$ tmp=" xy z"
$ a=$tmp
$ echo $a
$ echo "$a"
??解釋:什么時(shí)候會(huì)根據(jù) IFS 來"處理"呢?我覺得是荣刑,對(duì)于不加引號(hào)的變量馅笙,使用時(shí)都會(huì)參考IFS,但是要注意其原始值厉亏!
3.4 Example 4 :
#!/bin/bash
IFS_old=$IFS #將原IFS值保存董习,以便用完后恢復(fù)
IFS=$’\n’ #更改IFS值為$’\n’ ,注意爱只,以回車做為分隔符阱飘,IFS必須為:$’\n’
for i in $((cat pwd.txt)) #pwd.txt 來自這個(gè)命令:cat /etc/passwd >pwd.txt
do
echo $i
done
IFS=$IFS_old #恢復(fù)原IFS值
??另外一個(gè)例子,把IP地址逆轉(zhuǎn)輸出:
3.5 Example 5 :
#!/bin/bash
IP=220.112.253.111
IFS="."
TMPIP=$(echo $IP)
IFS=" " # space
echo $TMPIP
for x in $TMPIP ;do
Xip="${x}.$Xip"
done
echo ${Xip%.}
Complex_Example 1: http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=3660898&page=1#pid21798049
function output_args_ifs(){
echo "=$*"
echo "="$*
for m in $* ;do
echo "[$m]"
done
}
IFS=':'
var='::a:b::c:::'
output_args_ifs $var
??輸出為:
=::a:b::c:: # 少了最后一個(gè)冒號(hào)!看前面就知道為什么了
= a b c
[]
[]
[a]
[b]
[]
[c]
[]
??由于 "output_args_ifs var 沒有加引號(hào)沥匈,所以根據(jù)IFS替換蔗喂!根據(jù)IFS劃分出變量: "" "" "a" "b" "" "c" "" ""(可以通過輸出 $# 來測(cè)試參數(shù)的個(gè)數(shù)!)高帖,重組的結(jié)果為
?? "$@" 的值是 "" [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::"
??注意, @ 的值都是 "" "" "a" "b" "" "c" "" "" 预麸〉山可以說是一個(gè)列表……因?yàn)樗麄儽緛砭褪怯?
2 $3……組成的。
??所以吏祸,《Linux程序設(shè)計(jì)》里推薦使用 *
??參考文獻(xiàn):Shell中的IFS解惑