再谈 $* 和 [email protected] 在 Bash 中的表现

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

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

列表上下文

列表上下文是我们最熟悉的情况,比如在简单命令的参数中,又比如在 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

[email protected]

[email protected] 在列表上下文中的表现和 $* 在列表上下文中的表现完全一样。


$ set a "b c" d

$ printf "%s\n" [email protected]

a

b

c

d

"[email protected]"

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


$ set a "b c" d

$ printf "%s\n" "[email protected]"

a

b c

d

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

标量上下文

最常见的标量上下文就是赋值语句的右边,此外还有 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="$*" 完全一样。

[email protected]

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


$ set a "b c" d

$ IFS=:

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

$ echo "$var"

a b c d

"[email protected]"

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

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


$ set a "b c" d

$ IFS=:

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

0

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

0

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

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

时间: 2024-10-13 14:34:20

再谈 $* 和 [email protected] 在 Bash 中的表现的相关文章

[email protected], [email protected] 没有实体中的数据

public class AppServerAction extends BaseAction {    /**     *      */ /**      * 初始化 “我的产品”列表 JSP页面,具有分页功能      *       * @param request      * @param model      * @return      */      @Autowired    private StudentService stuservice;        private

[email protected]理解Angular中的$apply()以及$digest()

$apply() 和 $digest() 在 AngularJS 中是两个核心概念,但是有时候它们又让人困惑.而为了了解 AngularJS 的工作方式,首先需要了解 $apply() 和 $digest() 是如何工作的.这篇文章旨在解释 $apply() 和 $digest() 是什么,以及在日常的编码中如何应用它们. 探索 $apply() 和 $digest() AngularJS 提供了一个非常酷的特性叫做双向数据绑定 (Two-way Data Binding) ,这个特性大大简化了

[email protected]返回字符串中出现次数最多的那个字符和次数2

1 <!DOCTYPE html> 2 <html> 3 <head lang="en"> 4 <meta charset="UTF-8"> 5 <title>@返回字符串中出现次数最多的那个字符和次数2</title> 6 7 </head> 8 <body> 9 </body> 10 11 <script type="text/javasc

shell script中的$*和[email&#160;protected]

在shell script中,$*和[email protected]都是获取所有的命令行参数,但是这两者在使用的过程中会有细微的差别,差别主要是在有没有使用双引号,即是直接使用$*,[email protected],还是使用"$*","[email protected]". 直接使用$*,[email protected] #!/bin/bash count=1 for param in $*;do echo "\$* Parameter #$coun

23_Shell语言————位置变量([email&#160;protected]、$、$#、shift)

首先来看一个例子:计算100以内所有奇数的和以及所有偶数的和:分别显示之: [[email protected]]# vim odd_even.sh #!/bin/bash # EvenSum=0 OddSum=0   for I in {1..100}; do         if [ $[$I%2] -eq 0 ]; then                 EvenSum=$[$EvenSum+$I]         else                 OddSum=$[$OddSu

@bzoj - [email&#160;protected] [POI2015] Logistyka

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 维护一个长度为 n 的序列,一开始都是 0,支持以下两种操作: 1.U k a 将序列中第 k 个数修改为 a. 2.Z c s 在这个序列上,每次选出 c 个正数,并将它们都减去 1,询问能否进行 s 次操作. 每次询问独立,即每次询问不会对序列进行修改. input 第一行包含两个

rabbimq集群搭建报错:Error: unable TO perform an operation ON node &#39;[email&#160;protected]&#39;. Please see diagnostics information AND suggestions below.

在搭建rabbitmq集群的时候,添加内存节点时,抛出异常:Error: unable TO perform an operation ON node '[email protected]'. Please see diagnostics information AND suggestions below. 首先,假设当前服务器为A,我们在A服务器执行rabbitmqctl join_cluster [email protected] --ram,就是说我们要将A服务器作为内存节点加入到B中,然

make [email&#160;protected] $^ $&lt;

自动化变量,就是这种变量会把模式中所定义的一系列的文件自动地挨个取出,直至所有的符合模式的文件都取完了.这种自动化变量只应出现在规则的命令中. 下面是所有的自动化变量及其说明: [email protected] 表示规则中的目标文件集.在模式规则中,如果有多个目标,那么,"[email protected]"就是匹配于目标中模式定义的集合. $% 仅当目标是函数库文件中,表示规则中的目标成员名.例如,如果一个目标是"foo.a (bar.o)",那么,"

$^,[email&#160;protected],$?,$&lt;,$(@D),$(@F) of makefile

makefile下$(wildcard $^),$^,[email protected],$?,$<,$(@D),$(@F)代表的不同含义 $(filter-out $(PHONY) $(wildcard $^),$^)常用用法为$(wildcard *.c)表示列举当前目录下的所有.c文件这里$^因为会包含依赖的文件名,如果包含的该文件存在,那么将返回其含路径的文件名所以$(wildcard $^)就是用来过滤$^包含的所有文件并且该文件确实在本地存在. 自动化变量$?代表依赖文件列表中被改变