我们知道,在 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. openstack学习(三)创建虚拟机

    1 , 用图形管理界面创建虚拟机 #virt-manager    //打开图形管理界面 配置文件一般存放于/etc/libvirt/qemu 然后按照提示一步步安装即可,网络连接方式改为桥接 导出创 ...

  2. golang中的slice翻转存在以及map中的key判断

    //slice翻转 func stringReverse(src []string){ if src == nil { panic(fmt.Errorf("the src can't be ...

  3. JS重载

    Js 不支持函数的重载,可以用相同的名字在同一作用区域,定义两个函数,而不会引起错误,但真正使用的是最后一个. Js 不会验证传递给函数的参数个数是否和函数定义的参数的个数相同,开发人员定义的函数都可 ...

  4. Unity、Exception Handling引入MVP

    什么是MVP?在“MVP初探”里就有讲过了,就是一种UI的架构模式. 简单的描述一下Unity和Exception Handling Application Block: Unity是一个轻量级的可扩 ...

  5. Eclipse添加JPDL4 Schema校验

    由于jbpm官方提供的图形化流程设计器(GPD)功能并不是特别的全面,很多设计并不能全在图形界面下完成.因此,在很多情况下,我们需要直接编辑JPDL的XML源代码,所以, 最好为JPDL XML指定S ...

  6. .NET Core 1.0 RC2 历险之旅

    文章背景:对于.NET Core大家应该并不陌生, 从它被 宣布 到现在已经有1-2年的时间了,其比较重要的一个版本1.0 RC2 也即将发布..Net Core从一个一个的测试版到现在的RC2,经历 ...

  7. Swagger+AutoRest 生成web api客户端(.Net)

    简介 对于.net来说,用web api来构建服务是一个不错的选择,都是http请求,调用简单,但是如果真的要在程序中调用,则还有些工作要做,比如我们需要手写httpClient调用,并映射Model ...

  8. 如何理解vue.js组件的作用域是独立的

    vue.js组件的作用域是独立,可以从以下三个方面理解: 1.父组件模板在父组件作用域内编译,父组件模板的数据用父组件内data数据:2.子组件模板在子组件作用域内编译,子组件模板的数据用子组件内da ...

  9. Pyhont-Urllib2

    Urllib2 相当于的Urllib  的升级版 但又不能代替 Urllib 这个我得新手很费解呢...这个问题留着把,,等以后成大牛的时候在回来想想这儿问题!! Urllib2 常用 // 1 设置 ...

  10. 【BZOJ 3051】【UOJ #57】【WC 2013】平面图

    http://www.lydsy.com/JudgeOnline/problem.php?id=3051 http://uoj.ac/problem/57 这道题需要平面图转对偶图,点定位,最小生成树 ...