一、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 的值。

  1. $ echo $IFS
  2. $ echo "$IFS" | od -b
  3. 0000000 040 011 012 012
  4. 0000004
$ echo $IFS

$ echo "$IFS" | od -b
0000000 040 011 012 012
0000004

直接输出IFS是看不到的,把它转化为二进制就可以看到了,"040"是空格,"011"是Tab,"012"是换行符"\n" 。最后一个 012 是因为 echo 默认是会换行的。

2、$* 和 $@ 的细微差别
     从下面的例子中可以看出,如果是用冒号引起来,表示这个变量不用IFS替换!!所以可以看到这个变量的"原始值"。反之,如果不加引号,输出时会根据IFS的值来分割后合并输出! $* 是按照IFS中的第一个值来确定的!下面这两个例子还有细微的差别!

  1. $ IFS=:;
  2. $ set x y z
  3. $ echo $*
  4. x y z
  5. $ echo "$*"
  6. x:y:z
  7. $ echo $@
  8. x y z
  9. $ echo "$@"
  10. x y z
$ IFS=:;
$ set x y z
$ echo $*
x y z
$ echo "$*"
x:y:z
$ echo $@
x y z
$ echo "$@"
x y z

上例 set 变量其实是3个参数,而下面这个例子实质是2个参数,即 set "x y z"  和 set x y z 是完全不同的。

  1. $ set "x" "y z"
  2. $ echo $*
  3. x y z
  4. $ echo "$*"
  5. x:y z
  6. $ echo $@
  7. x y z
  8. $ echo "$@"
  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 $@
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

小结:$* 会根据 IFS 的不同来组合值,而 $@ 则会将值用" "来组合值!

3、for 循环中的奇怪现象

  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 ;done
0000000 012
0000001
0000000 040 141 012
0000003
0000000 142 012
0000002
0000000 012
0000001
0000000 143 012
0000002

先暂且不解释 for 循环的内容!看下面这个输出!IFS 的值同上! var=": a:b::c:"

  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 -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" 表示冒号),但是$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是默认值,那么处理的结果就很好算出来,直接合并、忽略多余空格即可!

另外,$* 和 $@ 在函数中的处理过程是这样的(只考虑"原始值"!)!"$@",就是像上面处理后赋值,但是 "$*" 却不一样!它的值是用分隔符(如":")而不是空格隔开!具体例子见最后一个例子!

好了,现在来解释 for 循环的内容。for 循环遍历上面这个列表就可以了,所以 for 循环的第一个输出是空!("012"是echo输出的换行符 )。。。。后面的依次类推!不信可以试试下面这个例子,结果是一样的!

  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 ;done
0000000 012
0000001
0000000 040 141 012
0000003
0000000 012
0000001
0000000 142 012
0000002
0000000 012
0000001
0000000 143 012
0000002

三、IFS的其他实例

Example 1:

  1. $ IFS=:
  2. $ var=ab::cd
  3. $ echo $var
  4. ab  cd
  5. $ echo "$var"
  6. ab::cd
$ IFS=:
$ var=ab::cd
$ echo $var
ab cd
$ echo "$var"
ab::cd

解释下:x 的值是 "ab::cd",当进行到 echo $x 时,因为$符,所以会进行变量替换。Shell 根据 IFS 的值将 x 分解为 ab "" cd,然后echo,插入空隔,ab[space]""[space]cd,忽略"",输出  ab  cd 。

Example 2 :

  1. $ read a
  2. xy  z
  3. $ echo $a
  4. xy  z
$ read a
xy z
$ echo $a
xy z

解释:这是 http://bbs.chinaunix.net/thread-207178-1-1.html 上的一个例子。此时IFS是默认值,本希望把所有的输入(包括空格)都放入变量a中,但是输出的a却把前面的空格给忽略了!!原因是:默认的 IFS 会按 space tab newline 来分割。这里需要注意的一点是,read 命令的实现过程,即在读入时已经替换了。解决办法是在开头加上一句 IFS=";" ,这里必须加上双引号,因为分号有特殊含义。

Example 3 :

  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 :

  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/bash
IFS_old=$IFS #将原IFS值保存,以便用完后恢复
IFS=$’\n’ #更改IFS值为$’\n’ ,注意,以回车做为分隔符,IFS必须为:$’\n’
for i in $((cat pwd.txt)) #pwd.txt 来自这个命令:cat /etc/passwd >pwd.txt
do
echo $i
done
IFS=$IFS_old #恢复原IFS值

另外一个例子,把IP地址逆转输出:

Example 5 :

  1. #!/bin/bash
  2. IP=220.112.253.111
  3. IFS="."
  4. TMPIP=$(echo $IP)
  5. IFS=" " # space
  6. echo $TMPIP
  7. for x in $TMPIP ;do
  8. Xip="${x}.$Xip"
  9. done
  10. echo ${Xip%.}
#!/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

  1. function output_args_ifs(){
  2. echo "=$*"
  3. echo "="$*
  4. for m in $* ;do
  5. echo "[$m]"
  6. done
  7. }
  8. IFS=':'
  9. var='::a:b::c:::'
  10. 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

输出为:

  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" "" ""(可以通过输出 $# 来测试参数的个数!),重组的结果为

"$@" 的值是  "" [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"  ""  "" 。可以说是一个列表……因为他们本来就是由 $1 $2 $3……组成的。

所以,《Linux程序设计》里推荐使用 $@,而不是$*

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

IFS解惑的更多相关文章

  1. 【转】SHELL中的IFS详解

    转自:http://smilejay.com/2011/12/bash_ifs/ 在bash中IFS是内部的域分隔符,manual中对其的叙述如下: IFS The Internal Field Se ...

  2. 关于 Word Splitting 和 IFS 的三个细节

    在 Bash manual 里叫 Word Splitting,在 Posix 规范里叫 Field Splitting,这两者指的是同一个东西,我把它翻译成“分词”,下面我就说三点很多人都忽略掉(或 ...

  3. [C#解惑] #2 对象的初始化顺序

    谜题 在上一篇C#解惑中,我们提到了对象的初始化顺序.当我们创建一个子类的实例时,总是会先执行基类的构造函数,然后再执行子类的构造函数.那么实例字段是什么时候初始化的呢?静态构造函数和静态字段呢?今天 ...

  4. [C#解惑] #1 在构造函数内调用虚方法

    谜题 在C#中,用virtual关键字修饰的方法(属性.事件)称为虚方法(属性.事件),表示该方法可以由派生类重写(override).虚方法是.NET中的重要概念,可以说在某种程度上,虚方法使得多态 ...

  5. Python 包管理工具解惑

    Python 包管理工具解惑 本文链接:http://zengrong.net/post/2169.htm python packaging 一.困惑 作为一个 Python 初学者,我在包管理上感到 ...

  6. 《浅谈磁盘控制器驱动》,磁盘控制器驱动答疑解惑![2012.1.29完结]by skyfree

    <浅谈磁盘控制器驱动>,磁盘控制器驱动答疑解惑![2012.1.29完结]  https://www.itiankong.net/thread-178655-1-1.html Skyfre ...

  7. use IFS in bash

    function dfd() { #http://www.cnblogs.com/hunterfu/archive/2010/02/23/1672129.html IFS=$'\n' for i in ...

  8. Matalab IFS分形算法

    IFS 算法代码 function IFS_draw(M,p) N=; :length(p); eval(['a',num2str(k),'=reshape(M(',num2str(k),',:),2 ...

  9. SAE上传web应用(包括使用数据库)教程详解及问题解惑

    转自:http://blog.csdn.net/baiyuliang2013/article/details/24725995 SAE上传web应用(包括使用数据库)教程详解及问题解惑: 最近由于工作 ...

随机推荐

  1. PHP实现对MongoDB的基础操作

    PHP扩展                                                                                      PHP5.2.PH ...

  2. Extjs 兼容IE10

    在对应的地方将Ext.isIE 修改成: Ext.isIE && !(/msie 9/.test(navigator.userAgent.toLowerCase())  &&a ...

  3. iOS之UISearchBar实时显示结果

    iOS之UISearchBar实时显示结果     UISearchBar 经常是配合UITableView 一起使用的,一般都将UITableView的tableHeaderView属性设置为UIS ...

  4. CentOS下安装JDK6u21和设置环境变量bin文件

    1.先通过SSH登录到Linux系统中,通过SSH文件管理工具把Linux的JDK安装包上传到/home/acm/JavaTools/JDK目录: 2.进入/home/acm/JavaTools/JD ...

  5. VS2010编译Qt程序失败------error LNK1123: 转换到 COFF 期间失败:

    error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏使用VS2010编译VC++项目的时候可能会出这个问题. 据说升级到SP1后可能问题解决,但是下载量太大,目前没有得到证实. ...

  6. linux下串口的阻塞和非阻塞操作

    有两个可以进行控制串口阻塞性(同时控制read和write):一个是在打开串口的时候,open函数是否带O_NDELAY:第二个是可以在打开串口之后通过fcntl()函数进行控制. 阻塞的定义: 对于 ...

  7. URAL 2038 Minimum Vertex Cover

    2038. Minimum Vertex Cover Time limit: 1.0 secondMemory limit: 64 MB A vertex cover of a graph is a ...

  8. 关于bootstrap--表单(下拉<select>、输入框<input>、文本域<textare>复选框<checkbox>和单选按钮<radio>)

    html 里面的 role 本质上是增强语义性,当现有的HTML标签不能充分表达语义性的时候,就可以借助role来说明.通常这种情况出现在一些自定义的组件上,这样可增强组件的可访问性.可用性和可交互性 ...

  9. poj 2407 Relatives(简单欧拉函数)

    Description Given n, a positive integer, how many positive integers less than n are relatively prime ...

  10. Linux内核设计基础(十)之内核开发与总结

    (1)Linux层次结构: (2)Linux内核组成: 主要由进程调度(SCHED).内存管理(MM).虚拟文件系统(VFS).网络接口(NET)和进程间通信(IPC)等5个子系统组成. (3)与Un ...