Shell 参数扩展:$# 与 ${#@}

Shell parameter expansion: $# vs. ${#@}

在 shell 脚本中,据我所知,$#${#@} 的行为相同,都给出了位置参数的数量。两者之间有什么区别吗?在什么情况下,一个比另一个更可取?

${#@} / ${#*}POSIX-likeshell中与$#相同,但是不是 all - 一个值得注意的例外是 dash,它在 Ubuntu 系统上充当 sh

$# 是 POSIX-compliant 形式,所以它是安全(便携)选择 (来自 POSIX spec,前缀 $隐含):

# Expands to the decimal number of positional parameters.


可选的背景信息

POSIX shell 规范主要基于历史上的 Bourne shell,其唯一的数组-like 构造是序列位置参数(</code>、<code>、...),其中 $# 包含 count 个位置参数,$* 扩展为space-separated 参数值列表,然后受 word-splitting 和 "$@" - 在 double-quoted 上下文中 - 扩展到最初指定的位置参数(即使它们包含嵌入的空格)。

下面分别讨论bashkshzshdash,其行为根本不同,在底部讨论。

bashkshzsh:

POSIX-compatible shell如kshbash后来推广了这个pseudo-array以提供真正的数组 变量,其语法借鉴了 positional-parameter 语法(zsh 也支持此语法,但也有自己的更简单的语法):

${arr[*]}"${arr[@]}"的功能类似于$*"$@"${#arr[@]}${#arr[*]}都对应于$#.

也许是为了向原始语法致敬,这些 shell(也包括 zsh,但其数组语法更简单)也选择支持 ${#@}${#*} for symmetry,你可以将 @ / * 视为 implied[ 的 all-elements 下标=121=]数组,即位置参数的pseudo-array。

关于元素的对称性提取:

  • 类似 ${@[2]} 来镜像 </code> 的东西 <em> 仅在 </em> <code>zsh 中起作用,而不在 bash 中起作用和 ksh.

  • 等效的切片语法适用于所有这些,但是:${@:2:1}


dash:

dash,Ubuntu 系统上的默认 shell (/bin/sh),dash,主要限于 POSIX-only 功能,并且根本不支持数组。

因此,它将 ${#@} / ${#*} 区别对待 :它将 @* 解释为 scalar string list 的(扩展)参数和 returns 该字符串的长度。
换句话说:在 dash 中,echo "${#@} / echo "${#*} 等同于:list="$@"; echo "${#list}".

在完全不支持数组的情况下,dash 既不支持 ${@[2]} 也不支持 ${@:2:1}