频道栏目
首页 > 资讯 > Linux > 正文

【Linux】IFS是个什么鬼

15-04-26        来源:[db:作者]  
收藏   我要投稿

一、IFS引入

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 substituioin.”。

IFS和参数输入牵扯颇多,举个最简单的例子,为什么给变量赋值的时候,如果有空格,需要将value用引号包起来。了解IFS,对shell编程助益颇多。

二、IFS,TMD撞鬼了

今天整理Linux环境变量的时候,发现粗大事了,IFS撞鬼了,从早上求证到现在,没有答案。

1. 背景

a. IFS放在哪里

登录系统之后,找寻IFS的身影。查找登录shell中全局环境变量,env |grep IFS,不在;查找登录shell中局部环境变量,set |grep IFS,存在。也就是说,IFS存在于登录shell的局部环境变量中

 

[work@localhost ~]$ env |grep IFS
[work@localhost ~]$ set |grep IFS
IFS=$' \t\n'

 

b. IFS用在哪里

 

结合上一篇博客《完全解读Linux环境变量》已验证的内容可知,局部变量的作用域只在当前的shell进程环境中,不能用在父shell环境,也不能用在子shell环境。那么IFS作为登录shell的局部环境变量,其作用域只在登录shell环境中。此处,再次验证一下,在登录shell换进各种设置局部变量testing="zhangsan",然后在脚本中访问该局部变量,如果访问不到,则证明局部变量没有被fork出的子shell环境拥有。

 

[work@localhost local]$ testing="zhangsan"
[work@localhost local]$ cat run.sh 
#!/bin/bash

echo $testing
[work@localhost local]$ sh run.sh 

[work@localhost local]$ 
最终执行脚本,没有访问到定义的局部变量testing,证明局部环境变量不能被子shell进程拥有

 

c. 没有个性化

 

结合上一篇博客《完全解读Linux环境变量》已验证的内容可知,启动shell、登录shell、交互shell,在进入的时候,都会预先加载、执行一些文件,这个过程可以理解成“环境初始化”或者“个性化”,当然,非交互shell(也就是执行脚本)也会有这么一个过程,即环境变量BASE_ENV,但是默认情况下这个环境变量没有设置,是不存在的,也就是说,通常情况下,执行脚本是没有个性化的。
 

2. 疑问

如果已经弄清楚背景所述的两个前提,那么问题来了。

-------------------------------------------------------------------------------

1. IFS作为登录shell的局部环境变量;

2. 局部环境变量的作用域是当前shell进程;

3. 执行脚本的时候,是没有个性化(或者”环境初始化“)的;

-------------------------------------------------------------------------------

problem:为什么脚本中可以使用IFS?

重述一遍问题:IFS作为登录shell的局部环境变量,为什么能在脚本中使用?

-------------------------------------------------------------------------------

事实上,你确实可以在IFS中使用,而且IFS的值和登录shell中是一致的,这不是粗大事撞鬼了吗?我是在是不能理解,搞了一天了也没弄明白,求大神解答!!!

三、IFS和$巧妙的苟合

说IFS和$苟合,这完全是诬陷。

1. 我们是两码事

IFS和$、单引号、双引号那是两码事,一个是男人,一个是女人,他们只是见了一面握了一下手,然后被不明真相的人看到了,就传说成了“苟合”。

IFS常常在read命令、参数扩展和命令替换中用于分隔数据,而$' '和$" "是bash shell中Quoting的语法规定。你可以通过man bash指令查看Quoting一节了解更详细的内容,下图摘选了与本文有关的部分内容截图。

鉴于CSND图片上传太烂了(后台在新老版本交替的情况下,bug无限啊),常常出现大小图的情况,我直接粘贴文本过来吧。

 

Words  of  the form $'string' are treated specially.  The word expands to string, with backslash-escaped characters replaced as specified
by the ANSI C standard.  Backslash escape sequences, if present, are decoded as follows:
	  \a     alert (bell)
	  \b     backspace
	  \e
	  \E     an escape character
	  \f     form feed
	  \n     new line
	  \r     carriage return
	  \t     horizontal tab
	  \v     vertical tab
	  \\     backslash
	  \'     single quote
	  \"     double quote
	  \nnn   the eight-bit character whose value is the octal value nnn (one to three digits)
	  \xHH   the eight-bit character whose value is the hexadecimal value HH (one or two hex digits)
	  \cx    a control-x character

The expanded result is single-quoted, as if the dollar sign had not been present.

A double-quoted string preceded by a dollar sign ($"string") will cause the string to be translated according to the current locale.   If
the  current  locale  is  C  or  POSIX, the dollar sign is ignored.  If the string is translated and replaced, the replacement is double-
quoted.

2. 我们在一起了

常常把IFS和$、单双引号放在一起讨论,一个原因是IFS的默认值定义IFS=$' \t\n',另一个主要原因就是大多数人在写shell脚本的时候,需要修改IFS的值,会纠结要不要加上“$“符号,加了能用,不加好像也是能用的,那到底是加还是不加呢?

加还是不加,根据需要来决定,因为中间有那么一小丢丢的差别。请往下看。

3. IFS、$和单引号

为了方便数据处理,在脚本中往往需要修改value,举个栗子,如果你不想使用默认值来分隔,而是需要使用逗号来分隔,你可以这么定义IFS=',',你也可以这么定义IFS=$',',你还可以这么定义IFS=$",",那他们有什么区别?首先不要把简单问题复杂化,这三句话都是Linux shell中的赋值语句,将左边的语句翻译后赋值给右边,所以直接探讨右边的事情会更直接(如以下代码所示),但是不好意思,在这里他们没区别,都是指代一个简简单单普普通通的逗号。

 

[work@localhost bin]$ echo ","
,
[work@localhost bin]$ echo $','
,
[work@localhost bin]$ echo $","
,
意外来了。IFS默认值是空格、制表符和换行符,但是如果你不想用那么多,只想要用制表符或者换行符中的一个来做分隔(这里以换行符来进行讨论,因为比较容易观察),你同样可以用以上三种方式进行IFS的重新赋值,IFS='\n',IFS=$"\n",IFS=$'\n',这一下就有区别了,第一个就是普通字符反斜杠和n,第二个是换行符,不过只有在执行的时候才会进行转化,第三个是一个已经转换了的回车符NL,在屏幕上直接就回车了。

 

 

[work@localhost bin]$ echo '\n' 
\n
[work@localhost bin]$ echo $"\n"
\n
[work@localhost bin]$ echo $'\n'


[work@localhost bin]$ 
回顾上文中man bash的定义,$、转义字符、单引号在一起,“Backslash escape sequences, if present, are decoded as follows:选项, The expanded result is single-quoted, as if the dollar sign had not been present.”,反斜线转译字符,如果这个字符存在于以下选项中,那么就进行解码,因此$'\n'最终被解码成了Linux的换行命令。

 

------------------------------------------------------------------------------------------------------------------------------

伪结论:

IFS的值如果是普通字符,那么加不加$都没有关系,因为表现是一致的;

IFS的值如果是系统定义的以上(截图或引用的代码中有示例)特殊字符,那么加上$是会被系统解码转译的,其表现的的确确就是按照换行符来分隔;不加那就是普通字符,但是在解析的过程中又被转译字符转译,所以在做分隔的时候,除了数据本身,”\n"也被认为是数据,因此也会被单独的分割出来,请看以下脚本调试信息,"\n"被当作数据对待,也被分隔出来了,但其本身不作为分隔符,因为转译字符的缘故,在被解析的时候解析成了换行。更多有趣的验证代码,在此不一一列举。

 

#!/bin/bash

IFS='\n'
str="a\nb\nc\nd\\n"
echo $str
for i in $str
do
        echo $i
done

echo "------"

str2="anbncndn"
echo $str2
for ii in $str2
do
        echo $ii
done
[work@localhost local]$ sh -x run2.sh 
+ IFS='\n'
+ str='a\nb\nc\nd\n'
+ echo a '' b '' c '' d ''
a  b  c  d 
+ for i in '$str'
+ echo a
a
+ for i in '$str'
+ echo

+ for i in '$str'
+ echo b
b
+ for i in '$str'
+ echo

+ for i in '$str'
+ echo c
c
+ for i in '$str'
+ echo

+ for i in '$str'
+ echo d
d
+ for i in '$str'
+ echo

+ echo ------
------
+ str2=anbncndn
+ echo a b c d
a b c d
+ for ii in '$str2'
+ echo a
a
+ for ii in '$str2'
+ echo b
b
+ for ii in '$str2'
+ echo c
c
+ for ii in '$str2'
+ echo d
d
------------------------------------------------------------------------------------------------------------------------------

 

结论:

1. IFS中的字符可以是空白符(空格、制表符、回车符)和非空白符,如果是空白符,则数据前后的空白符会被忽略;如果是非空白符,则不会被忽略;

2. 如果有多个连续的空白符,则并列成为一个分隔符,而多个连续的非空白符,则被认为是多个分隔符;

------------------------------------------------------------------------------------------------------------------------------

4. IFS、$和双引号

在Linux shell中的表现和没有$符号一致,此处我也理解不深刻,略了,sorry···

5. 为苟合正名

IFS和$在一起,不是苟合

相关TAG标签
上一篇:如何修改mysql5.6.24解压缩版(免安装版或zip版)字符编码
下一篇:【SQLServer2008商务智能BI】数据挖掘导论
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站