除非特别说明,本文中出现的 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一次诡异的优化

    最近做的项目快上线了,在做了一次压力测试后发现了不少问题,基本都是因为数据量达到一定级别时(预测系统上线10年后的数据量)SQL查询超时,其中有些是因为索引缺失.有些是因为写法不好,这些在有经验的人眼 ...

  2. linux中的权限对于文件和目录的重要性

    对于文件 r 可以读取文件的实际内容 w 可以编辑文件的内容 x 文件可以被系统执行 对于目录 r 具有读取目录的结构列表,也就是说你可以用ls命令查看目录下的内容列表 w 可以建立新的文件,删除文件 ...

  3. 如何正确的使用jquery-ajax

    什么是ajax ajax全称Asynchronous Javascript And XML,就是异步javascript和xml ajax的作用 ajax通常用于异步加载网页内容,以及局部更新. 实际 ...

  4. Tomcat 启动花费很长时间的解决方案

    原始解决方案链接 将 $JAVA_PATH/jre/lib/security/java.security 中的 securerandom.source=file:/dev/urandom 替换为 se ...

  5. JS入门学习,写一个简单的图片库

    <!-- 新手刚开始学JS,每天坚持写点东西 坚持下去,希望能有所进步 .  加油~~ --> <!DOCTYPE html>                         ...

  6. Serial Port Programming on Linux(转载)

    This is a tutorial on how to program the Serial Ports on your Linux box.Serial Ports are nice little ...

  7. OpenFlow:Enabling Innovation in Campus Networks

    SDN领域,OpenFLow现在已经成为了广泛使用的南向接口协议.若想好好学习SDN,在这个领域有所进步,需要熟悉OpenFlow协议.我最近找了篇有关OpenFLow的论文,发现最早该协议是在Sig ...

  8. [No000070]Flash与C#通信

    注册控件 首先要插入Flash就必须使用Flash控件,在工具栏右键选择"选择项…",然后在"COM组件"面板下点击"浏览"按钮,在本机电脑 ...

  9. 修改eclipse的自动完成功能

    修改eclipse的自动完成功能   周银辉 用eclipse时还是比较习惯Visual Studio那样的敲一个字母就弹出自动完成框,而不是总要等到敲.号,其实可以设置的: 在preferences ...

  10. Android5.0资源 colorAccent,colorPrimary,colorPrimaryDark