一、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有什么用?IFS的默认值是空格、制表符和换行符,此处不再赘述,请参看:

《Shell中的IFS解惑》

《shell中的IFS变量》

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/bashecho $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中是一致的,这不是粗大事撞鬼了吗?我是在是不能理解,搞了一天了也没弄明白,求大神解答!!!

3. 解答

Linux中每个进程都有自己的环境(main函数的char *env[]参数指向),环境是由一组变量组成的,这些变量中存有进程可能需要引用的上下文信息。bash将环境变量的复本保存在variables.c中名为shell_variables的全局VAR_CONTEXT结构中。要导出给子进程的变量由全局字符串指针char **export_env记录,形式是“名=值”字符串数组,也就是键入export命令看到的内容。

bash启动后,调用variables.c中的initialize_shell_variables()函数,传入来自main函数的env参数,将env中的环境变量存入shell_variables。对于PATH、IFS、PS1之类bash本身要使用的环境变量,如果env中尚无,则在此时建立。另外一些有关bash版本、命令历史、邮件检查等内部辅助功能的环境变量也在这里建立。

execute_cmd.c中调用各类命令的函数在执行命令之前,首先调用variables.c中的maybe_make_export_env()函数,构建导出给子进程的环境,即export_env。shell_execve()执行外部命令时使用的是exec族中的execve()函数,因此可以将export_env传递给bash启动的子进程。

凡需要增改环境变量的地方,调用variables.c中的bind_variable()函数实现。例如在cd命令执行后需要重设PWD。

摘自:点击打开链接

三、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 characterThe 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/bashIFS='\n'
str="a\nb\nc\nd\\n"
echo $str
for i in $str
doecho $i
doneecho "------"str2="anbncndn"
echo $str2
for ii in $str2
doecho $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. 如果有多个连续的空白符,则并列成为一个分隔符,而多个连续的非空白符,则被认为是多个分隔符;

         3. 如果你定义的IFS值和系统预留的特殊字符相同,并且你希望其能预先解码成系统特殊字符使用,那么请用$修饰;否则,其效果和直接在code中写特殊字符一样,会等到使用的时候才转码成系统特殊字符;

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

4. IFS、$和双引号

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

5. 为苟合正名

IFS和$在一起,不是苟合

附注:

本文如有错漏,烦请不吝指正,谢谢!

本文第二节所提疑问,烦请大神不吝赐教,在下不甚感激!

【Linux】IFS是个什么鬼相关推荐

  1. C语言: ---Linux下ulimit是什么鬼

    其实ulimit的讲解不属于C或者C++ 语言范畴,他只是在我们日常开发或者线上linux运行环境不可缺少的工具. 比如我们要查看服务器崩溃的core文件,允许core文件产生,都需要ulimit - ...

  2. 如何编译Linux内核文件

    如何编译Linux内核文件 参考:朱有鹏Uboot的全集 前言:我们的Linux内核文件动则数万个文件,很多个子文件夹,当然是使用Makefile管理了,但是是不是真的仅仅只是make一下就可以了呢? ...

  3. linux笔记:MOOC Linux开发环境及应用

    文章目录 1. linux入门 1.1 实用程序 1.2 了解系统状态 2. 正则表达式 2.1 元字符 2.2 正则表达式拓展 2.3 常用命令 3. 文件编辑 3.1 vim常用操作 3.2 常见 ...

  4. OpenCV中使用类VideoCapture加载视频和打开摄像头

    先直接上示例代码,然后对代码进行讲解. #!/usr/bin/env python # -*- coding: utf-8 -*- # 图像处理开发需求.图像处理接私活挣零花钱,请加微信/QQ 248 ...

  5. OpenCV类VideoCapture构造函数中参数apiPreference的可选值及意义

    OpenCV类VideoCapture构造函数中参数apiPreference的可选值及意义 OpenCV的类VideoCapture有三种构造函数,原型如下. C++: cv::VideoCaptu ...

  6. Linux之特殊的环境变量IFS以及如何删除带有空格的目录

    1.IFS是什么? Linux下有一个特殊的环境变量叫做IFS,叫做内部字段分隔符(internal field separator).IFS环境变量定义了bash shell用户字段分隔符的一系列字 ...

  7. Linux之IFS间隔符、C编程、gdb调试

    1 间隔符 在shell下的for循环语句中,容器中,各个元素之间 是通过 间隔符 来分开的 默认情况下:读取 列表中的值----以 空格.TAB.换行  为间隔符 列表可以为:     常量    ...

  8. linux中分隔符IFS

    在linux中IFS是分隔符的意思,linux中变量分两种,全局变量env和局部变量set,set中包含了env的全部变量,我们查看IFS的值发现env | grep IFS为空,而set | gre ...

  9. linux shell ifs,Shell 的 IFS 变量

    今天把一个 shell script 从 Linux 移植到 Solaris 时遇到一些小问题: args=`tail -n 1 $file | head -1` tail 的用法有点不一样.Sola ...

最新文章

  1. 定制或外购适配器规格需求列表
  2. 国家开放大学2021春1107传感器与测试技术题目
  3. Java类class forName()方法及示例
  4. 解决域中打印机不能共享打印的问题
  5. spring3.0 aop 获取 ibatis 执行的语句_Mybatis 源码分析:执行器
  6. mysql滴的一声就关了_关于mysql数据库在输入密码后,滴的一声直接退出界面的解决办法(详细办法)...
  7. 东北大学oj平台python答案_东北大学大数据班R实训第三次作业
  8. mixins,generics(ApiView)
  9. 安卓源代码_如何从在安卓Android手机获取微信小程序源代码
  10. 5步操作,解决SOLIDWORKS处理复杂零件时卡顿的问题
  11. jsp js弹出网页对话框
  12. Android TextView水平跑马灯
  13. 不小心隐藏IDEA的main menu,让它恢复显示的解决方法
  14. 蛤蟆 Oracle,19.蛤蟆的Oracle杂记——数据字典dba_views
  15. 人机协作机器人发展趋势_协作机器人未来发展趋势怎么样?
  16. 筛法打印质数表【附:100000以内质数表】
  17. 『一起学AI』生成对抗网络(GAN)原理学习及实战开发
  18. 莫纳什计算机专业优势,2020年去澳洲留学就读莫纳什大学计算机学院有哪些优势?...
  19. 点卯.三维视频融合助力国家全面推进实景三维中国建设, 构建数字中国建设基座 ;开放C++源代码 点卯-魔镜系列
  20. PHP实现生成小程序二维码带参数进入指定页面、小程序URL scheme实现携带数据跳转小程序

热门文章

  1. 宝塔linux升级,宝塔linux面板去除后台强制更新
  2. 2018/01/22 爬虫日记
  3. 女人:不爱,请收起你的暧昧
  4. GAMES101-现代计算机图形学学习笔记(作业01)
  5. 洛谷 P1491 集合位置
  6. java打怪升级地图
  7. 米兔机器人自主编程_米兔积木机器人编程指南.pdf
  8. 数据分析为什么要学Excel
  9. Android仿英雄联盟/斗鱼波形加载动画
  10. 2018年SCI论文--整合GEO数据挖掘完整复现 八 :STRING数据库构建蛋白质相互作用网络(PPI),cytoscape软件筛选hub基因