除非特别说明,本文中出现的 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. sql server几种读写分离方案的比较

    在生产环境中我们经常会遇到这种情况: 前端的oltp业务很繁忙,但是需要对这些运营数据进行olap,为了不影响前端正常业务,所以需要将数据库进行读写分离. 这里我将几种可以用来进行读写分离的方案总结一 ...

  2. mysql在linux下的安装

    安装环境:系统是 centos6.5 1.下载 下载地址:http://dev.mysql.com/downloads/mysql/5.6.html#downloads 下载版本:我这里选择的5.6. ...

  3. CentOS 6.5下Redis安装记录

    Redis简介: Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API.从2010年3月15日起,Redis的开发工 ...

  4. [转]ASP.NET Core 之 Identity 入门(三)

    本文转自:http://www.cnblogs.com/savorboard/p/aspnetcore-identity3.html 前言 在上一篇文章中,我们学习了 CookieAuthentica ...

  5. Java JDBC基础学习小结

    JDBC是一个Java应用程序接口,作用是封装了对数据库的各种操作.JDBC由类和接口组成,使用Java开发数据库应用都需要4个主要的接口:Driver.Connection.Statement.Re ...

  6. 第13章 Java常用类

    1.自动装箱和自动拆箱 自动装箱:基本类型就自动的封装到与它相同类型的包装中:如: 创建一个对象时:Integer i = 100;本质上是编译器编译时为我们添加了:Integer i = new I ...

  7. sqlserver 2008R2数据库迁移oracle

    x项目需要,将以前的sqlserver数据库迁移的oracle数据库中,由于以前对oracle只是在DML语句的步骤,所以总结一下这次遇到的问题以及具体步骤 1,oracle新建数据库 新建Oracl ...

  8. WPF MultiRangeSlider 简单实现

    WPF 多滑块Slider简单实现(MultiRangeSlider) WPF中的MultiRangeSlider,网上有商业的,有开源的,找了几个都不太理想,那就自己写 一个吧,给大家提供点思路. ...

  9. .Net配置中心-Zookeper版

    简介    zookeeper的基本概念和作用这里不做介绍,现在很多的公司都在使用它,说起它的作用,可能最先想到的是配置中心,可以将配置项作为一个node存储在zookeeper中,其他应用可以“关注 ...

  10. 吉特仓库管理系统- 斑马打印机 ZPL语言的腐朽和神奇

    上一篇文章说到了.NET中的打印机,在PrintDocument类也暴露一些本质上上的问题,前面也提到过了,虽然使用PrintDcoument打印很方便.对应条码打印机比如斑马等切刀指令,不依赖打印机 ...