除非特别说明,本文中出现的 Shell 均指 Bash 4.3。首先说一个基础知识:Shell 中的变量在展开成值(Parameter Expansion)之后,这个值在某些上下文(Context)中,还会进行分词操作(Word Splitting),但在另外一些上下文中,不会进行分词操作。本文中把会进行分词操作的上下文叫做列表上下文(List Context),把不会进行分词的上下文叫做标量上下文(Scalar Context)。还有一个基础知识再提一嘴,就是 Shell 在分词时会跳过那些被双引号包围的词。

因为 $* 和 $@ 这两个特殊变量在以上两种上下文中的展开结果不一样,所以下面必须分两种情况讨论。

列表上下文

列表上下文是我们最熟悉的情况,比如在简单命令的参数中,又比如在 for-in 语句的参数中,这些地方需要的都是多个词,所以 Shell 规定在这些地方要进行分词操作。

$*

$* 在列表上下文中会展开成 $1 $2 $3 ... 多个词,而又因列表上下文存在分词操作,所以 $1 $2 等等都会再被 IFS 分割。

$ set a "b c" d

$ printf "%s\n" $* # $2 的值为 "b c",但由于 $2 本身没有被双引号包围,所以会被分成两个词 b c,所以一共就成了 a b c d 四个参数

a

b

c

d

"$*"

"$*" 在列表上下文中会展开成 "$1c$2c$3...",c 是 IFS 的第一个字符,如果 IFS 为空,则 c 也是空,如果 IFS 不存在,则 c 为空格,虽然这里存在分词操作,但由于展开后的值仍处于双引号中,所以分词操作不会有任何效果。

$ set a "b c" d

$ IFS=:

$ printf "%s\n" "$*" # 所有位置参数连接成了一个参数

a:b c:d

$@

$@ 在列表上下文中的表现和 $* 在列表上下文中的表现完全一样。

$ set a "b c" d

$ printf "%s\n" $@

a

b

c

d

"$@"

"$@" 在列表上下文中会展开成 "$1" "$2" "$3" ...,由于展开后的每个值都处于双引号中,所以分词操作不会有任何效果。

$ set a "b c" d

$ printf "%s\n" "$@"

a

b c

d

列表上下文是我们最熟悉的,Bash manual 对 $* 和 $@ 的讲解也仅限于列表上下文中的表现,下面我们讲讲它们俩在标量上下文中的表现。

标量上下文

最常见的标量上下文就是赋值语句的右边,此外还有 case 关键字的后面,以及 [[ ]] 之间等等,这些地方需要的都是一个词,所以 Shell 规定在这些地方不进行分词操作。

$*

$* 在标量上下文中展开成 $1c$2c$3...,c 是 IFS 的第一个字符,由于标量上下文没有分词操作,所以这就结束了,也就是说,$* 在标量上下文的效果等同于 "$*" 在列表上下文中的效果。

$ set a "b c" d

$ IFS=:

$ var=$* # var 的值成了 "a:b c:d"

$ echo "$var"

a:b c:d

"$*"

"$*" 在标量上下文中展开成 "$1c$2c$3...",由于反正没有分词操作,所以和 $* 在标量上下文中的表现一样。所以也就是说 var=$* 和 var="$*" 完全一样。

$@

$@ 在标量上下文展开成 $1空格$2空格$3...,这里用“空格”字样是为了说明展开后的值是一个词。也就是说,$@ 和 $* 在标量上下文下的区别仅仅是前者用空格做分隔符后者用 IFS 的第一个字符做分隔符这一个区别。

$ set a "b c" d

$ IFS=:

$ var=$* # var 的值成了 "a b c d",不使用 IFS

$ echo "$var"

a b c d

"$@"

"$@" 在标量上下文中展开成 "$1空格$2空格$3..." 和不加引号效果一样,var=$@ 等效于 var="$@"。

再总结一下就是,在标量上下文中,$* 和 $@ 加不加引号都一样,它俩的区别就是分隔符的区别,它俩展开后的结果都是用一个分隔符把所有位置参数连接成了一个词。下面再用 [[ ]] 的代码示例巩固一下它俩的区别:

$ set a "b c" d

$ IFS=:

$ [[ "$*" == "a:b c:d" ]]; echo $?

0

$ [[ "$@" == "a b c d" ]]; echo $?

0

在实际编码中没必要记忆这些区别,你只需要记住一点,需要多个词的时候用 "$@",需要一个词的时候用 "$*",是的,永远带着引号。此外,由于 Posix 规范明确规定了“本规范不对 $@ 在标量上下文上的表现做任何定义”,所以上面的一些代码示例在 Bash 以外的 Shell 上可能有不同的结果。

最后一句,$* 和 $@ 的所有表现都应该能推广到带 * 和 @ 下标的任意数组上。

再谈 $* 和 $@ 在 Bash 中的表现的更多相关文章

  1. 『忘了再学』Shell基础 — 9、Bash中的特殊符号(一)

    目录 1.双单引号 2.双引号 3.$符号 4.反引号 5.$()符号 6.#符号 7.\符号 1.双单引号 '':单引号.在单引号中所有的特殊符号,如$和"`"(反引号)都没有特 ...

  2. 『忘了再学』Shell基础 — 10、Bash中的特殊符号(二)

    提示:本篇文章接上一篇文章,主要说说()小括号和{}大括号的区别与使用. 8.()小括号 ():用于一串命令执行时,()中的命令会在子Shell中运行.(和下面大括号一起说明) 9.{}大括号 {}: ...

  3. Bash中的任务(job)管理

    本来不准备写这篇博客的,因为任务管理(job管理)非常非常常用,以至于觉得根本没有必要去写这样一个东西.但想了下,还是记录一下吧,也许有人会用到呢. 不知你是否碰到过这样的情况,当你兴致勃勃的打开VI ...

  4. bash中的数值运算

    第一种,使用((表达式)): a=3 ((b=a+2)) echo $b 第二种使用let: let "c=$a+4" echo $c 第三种,使用expr表达式(注意空格不能少) ...

  5. echo $[1 + 2] shell中 $[] 在bash中同$(()),用于算术计算

    shell脚本编写:echo $[ 11#8+1] 输出结果是几,为什么,怎么算来的?  摘自:https://zhidao.baidu.com/question/334766451.html   结 ...

  6. bash中前后移动一个单词和删除单词的快捷键

    bash中一个很重要的快捷键,就是向后删除一个单词: ctrl+w=ctrl+W 一个字符一个字符的移动是: ctrl+f, ctrl+b 但是, 一个单词一个单词的移动是: (但是, 这个用得比较少 ...

  7. Bash 中的 $0 在什么时候不是 argv[0]

    每个 C 程序都有一个 main 函数,每个 main 函数都有一个 argv 参数,这个参数是一个字符串数组,这个数组的值是由该 C 程序的父进程在通过 exec* 函数启动它时指定的. 很多人说 ...

  8. ORACLE 查询一个数据表后通过遍历再插入另一个表中的两种写法

    ORACLE 查询一个数据表后通过遍历再插入另一个表中的两种写法 语法 第一种: 通过使用Oracle语句块  --指定文档所有部门都能查看 declare cursor TABLE_DEPT and ...

  9. bash中不可以用字符串做数组下标

    bash中可以用字符串做数组下标吗例如 test["abc"]=1------解决方案-------------------- 好像是误会,是awk里可以,bash shell里不 ...

随机推荐

  1. hammer.js的六大事件

    1.Pan事件:在指定的dom区域内,一个手指放下并移动事件,即触屏中的拖动事件.这个事件在触屏开发中比较常用: Panstart 拖动开始 Panmove 拖动过程 Panend 拖动结束 Panc ...

  2. [转]不优雅的方式处理 xlrd 中 int/float 的问题

    原址:http://blog.chedushi.com/archives/7258 最近在用 xlrd 写一个题库自动导出的程序,但碰到一个比较 ugly 的问题. 程序要求是将 xls 文件中的数据 ...

  3. Neutron 理解 (6): Neutron 是怎么实现虚拟三层网络的 [How Neutron implements virtual L3 network]

    学习 Neutron 系列文章: (1)Neutron 所实现的虚拟化网络 (2)Neutron OpenvSwitch + VLAN 虚拟网络 (3)Neutron OpenvSwitch + GR ...

  4. POJ3422 Kaka's Matrix Travels[费用流]

    Kaka's Matrix Travels Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 9522   Accepted:  ...

  5. UVA - 1025 A Spy in the Metro[DP DAG]

    UVA - 1025 A Spy in the Metro Secret agent Maria was sent to Algorithms City to carry out an especia ...

  6. Ubuntu14.0下安装Zend Framework 2

    Ubuntu14.0下安装Zend Framework 2为了安装这个东西,忙活了快一天了,参考中文博客一直没有安装成功,有些博客的时间也是已经很早了,后来google看英文版的才安装成功,这里记录一 ...

  7. (转载)SQL删除重复数据方法

    本文转载自http://www.cnblogs.com/sunxiaonan/archive/2009/11/24/1609439.html 例如: id           name         ...

  8. 【UE】关于UE的一个真实案例

    看到下面这个页面,你心里是怎么想的?我看到后,我心里会有疑问:咦,是不是程序错了?如果是程序错了,我应该怎么补偿呢?怎么没有刷新按钮? 针对以前几点问题,做以下几点改进: 1.在没有数据时,增加提示, ...

  9. OrchardNoCMS实体关系映射扩展

    在OrchardNoCMS中,默认的系统会把实体关系映射保存到mappings.bin文件中. 如果不进行任何修改,默认的可以自动保存关系映射的model是有很大限制的. 条件是model的命名空间必 ...

  10. Android开发自学笔记(Android Studio)—4.4 AdapterView及其子类

    一.引言       AdapterView本身是一个抽象类,而它派生的子类在用法上也基本相似,只是在显示上有一定区别,因此把他们也归为一类.       AdapterView具有如下特征: Ada ...