我们知道,在 Shell 中,一个变量在被展开后,如果它没有被双引号包围起来,那么它展开后的值还会进行一次分词(word splitting,或者叫拆词,分词这个术语已经被搜索引擎相关技术占用了)操作,分成几个词,传给命令的就是几个单独的参数。

举个例子,比如:

$ foo="1 2 3"

$ how_many_args() { echo $#; }

$ how_many_args $foo

3

$ how_many_args "$foo"

1

但是当这个变量出现在 here string 中时,这个分词操作该不该进行?毕竟 here string 的目的是把这个字符串作为整体传给当前命令的标准输入,在这里分词是没有意义的。Bash 文档中也说到:

3.6.7 Here Strings

A variant of here documents, the format is:

<<< word

The word undergoes brace expansion, tilde expansion, parameter and variable expansion, command substitution, arithmetic expansion, and quote removal. Pathname expansion and word splitting are not performed. The result is supplied as a single string to the command on its standard input.

然后 Bash 的实际实现却不是这样的:

$ foo="1|2|3"

$ IFS='|'

$ cat <<< "$foo"

1|2|3

$ cat <<< $foo

1 2 3

这条命令在其他两个支持 here string 的大众 shell ksh 和 zsh 输出的都是 foo 的原值 1|2|3,唯独 Bash 对它进行了分词,然后又用空格 join 成了字符串,所以成了1 2 3。

这个 bug 在 Bash 首次实现 here string 13 年之后直到昨天才被人发现,Bash 作者表示下个版本会修复。我在想为什么这个 bug 这么久才被发现的同时(也许大部分人都习惯在使用变量的时候包上双引号了),又在想为什么 Bash 的文档是对的,实现确是错的,这说明作者是按照某个规范实现的,文档是抄来的,所以没错。然后我查了下 Posix 规范的 Redirection 部分,发现规范里只有 here document,没有 here string。那可能是我猜错了,我又推测:这个语法是 Bash 发明的,只是作者没写测试代码,实现的时候出错了。

网上资料很少,我使劲谷歌了下,发现一本 93 年出版的书 Learning the Korn Shell 详细记载了 hree string 这个特性的来龙去脉

This notation first originated in the Unix version of the rc shell, where it is called a "here string." It was later picked up by the Z shell, zsh (see Appendix A), from which the Korn shell borrowed it. This notation is simple, easy to use, efficient, and visually distinguishable from regular here-documents.

原来是 rc shell(1989)年首次发明了 here document 的变种 here string,后来 zsh (1990)学了过来,然后 ksh(1993)也借了过来,最终 Bash 在 2002 年才抄了过来。 有一点想说的是,我本以为 zsh 这种被人称为终极 shell 的 shell 怎么都是 21 世纪的产物吧,原来 90 年就有了,学习了。

Here String 中不该进行分词的更多相关文章

  1. solr服务中集成IKAnalyzer中文分词器、集成dataimportHandler插件

    昨天已经在Tomcat容器中成功的部署了solr全文检索引擎系统的服务:今天来分享一下solr服务在海量数据的网站中是如何实现数据的检索. 在solr服务中集成IKAnalyzer中文分词器的步骤: ...

  2. Java的String中的subString()方法

    方法如下: public String substring(int beginIndex, int endIndex) 第一个int为开始的索引,对应String数字中的开始位置, 第二个是截止的索引 ...

  3. C++string中有关字符串内容修改和替换的函数浅析

    1.assign() 原型: //string (1) basic_string& assign (const basic_string& str); //substring (2) ...

  4. 从源代码的角度聊聊java中StringBuffer、StringBuilder、String中的字符串拼接

    长久以来,我们被教导字符串的连接最好用StringBuffer.StringBuilder,但是我们却不知道这两者之间的区别.跟字符串相关的一些方法中总是有CharSequence.StringBuf ...

  5. Java-J2SE学习笔记-查找一个String中,subString的出现次数

    1.查找一个String中,subString的出现次数 2.代码 package Test; public class TestStringContain { public static void ...

  6. string中常用的函数

    string中常用的函数 发现在string在处理这符串是很好用,就找了一篇文章放在这里了.. 用 string来代替char * 数组,使用sort排序算法来排序,用unique 函数来去重1.De ...

  7. String 中的秘密

    Navigation:  数据类型相关 > Delphi 的字符及字符串 > [3] - String 中的秘密   //String 的指针地址及实际的内存地址 var str: str ...

  8. String中的==与Empty

    1.String中的==与Equals方法执行结果一样吗? 我们都知道对于引用类型"=="比较的是引用而不是具体的值,但c#中有一种神奇的叫做操作符重载的东西.官方对String类 ...

  9. Java 字符串比较,String 中的一些方法 == 和 equals 的详解

    "==" 是比较的是两个对象的内存地址,而equals方法默认情况下是比较两个对象的内存地址. 1.String str = "hello"  生成的字符串,首 ...

随机推荐

  1. Walle - 瓦力上线部署系统搭建攻略

    背景知识:Walle - 瓦力是一个支持svn.git.多用户.多项目.多环境同时部署的上线部署系统,http://www.oschina.net/news/68610/walle-0-9-2 实验系 ...

  2. 用修改hosts的方式来屏蔽某些网站

    首先呢,来自知乎的一些问答:修改 hosts 文件可以上一些墙内屏蔽的网站,其原理是什么? (然而通过增/改hosts的方法来FQ已经失效很久了) 然后呢,也可以通过修改hosts来达到屏蔽某些网站的 ...

  3. Excellent Articles

    Lisp The roots of lisp Recursive Functions of Symbolic Expressions and Their Computation by Machine, ...

  4. Learn Python The Hard Way ex41中的程序

    import random from urllib import urlopen import sys WORD_URL = "http://learncodethehardway.org/ ...

  5. 三维网格补洞算法(Poisson Method)

    下面介绍一种基于Poisson方程的三角网格补洞方法.该算法首先需要根据孔洞边界生成一个初始化补洞网格,然后通过法向估算和Poisson方程来修正补洞网格中三角面片的几何形状,使其能够适应并与周围的原 ...

  6. jquery的add()用法总结

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="utf-8&quo ...

  7. Laravel五大功能之Eloquent关系模式

    Eloquent是Laravel的原始ActiveRecord是实现的,建立在Laravel的Fluent Query Builder之上的,所以Eloquent类和Fluent类是一样的,能实现复杂 ...

  8. php 时间加减

    <?php date_default_timezone_set('PRC'); //默认时区 echo "今天:",date("Y-m-d",time() ...

  9. 使用Memberane Moniter监控HTTP & SOAP requests

    Memberane Moniter 使用方法见左侧Documentation 此工具可以监控到每一次发生在指定端口的http请求或者soap请求,如图所示. 但是个人认为仍然有几个问题: 1.不能真正 ...

  10. OrchardNoCMS vNext如何在VS2015下调试

    由于VS2015还没有出来正式版,ASP.NET 5也是没有出来正式版.所以在你下载了OrchardNoCMS vNext的代码后,需要配置一些东西,才能启动项目,开始调试. 需要注意以下几点: 1. ...