我读到位置参数从 开始$1
(例如:$1
、$2
等$3
是位置参数)。但$0
不是位置参数。
但为什么$0
不是位置参数呢?
我认为这可能是一个原因,但不确定:
位置参数仅在执行脚本时才取其值。例如:如果我们这样做./myScript Hello
,那么$1
就会有值Hello
。但$0
可以在两种情况下获取其值:执行脚本时(它将具有脚本名称的值),以及当bash
其本身在没有脚本的情况下执行时(它将具有值bash
或-bash
)。
答案1
@ikkachu 已经解释过了比我更好。我只是添加一个历史记录。
Unix 第一个版本附带的 shell(后来称为 Thompson shell)没有变量,但您已经可以编写带有参数的简单脚本。
sh [ name [ arg1 ... [ arg9 ] ] ] The name is the name of a file which will be read and in‐ terpreted. If not given, this subinstance of the shell will continue to read the standard input file. In command lines in the file (not in command input), character sequences of the form "$n", where n is a digit 0, ..., 9, are replaced by the nth argument to the invo‐ cation of the shell (argn). "$0" is replaced by name.
$1
...$n
已经是脚本的参数和$0
名称(不是第一个参数)但没有被调用位置参数然后。
请注意,当时,$1
在 shell 解释之前,它实际上被替换为第一个参数。
例如,一个脚本具有:
echo $1
称为
sh script 'foo; echo bar'
会跑echo foo; echo bar
。该 shell 是一个非常简单的 shell,是为具有几百 KB 内存的计算机编写的。
大约十年后(70 年代末),Bourne shell 与 Unix 版本一起出现,引入了环境和其他优点。
Bourne shell 确实带有变量和更多的编程结构。
这位置参数术语,至少在涉及 Unix shell 时,是由 Bourne shell 引入的,并且指的是相同的东西,$1
...$n
用于脚本的参数(仍然$0
是脚本名称)。就像在 Thompson shell 中一样,您只能使用位置参数引用前 9 个参数($1
to $9
)(但使用 shift 或"$@"
或for i do
循环来访问其余部分)(这也解释了(向后可移植性)为什么您需要${10}
而不是$10
在大多数现代中sh
第 10 个的实现)。
这次,sh script 'foo; echo bar'
不再导致echo bar
运行,但 Bourne shell 仍然引入了臭名昭著的 split+glob,我想不会破坏与 Thompson shell 的向后兼容性太多,这样script 'foo *'
哪里script
仍然可以echo $1
调用以及文件列表当前目录作为参数(就像在 Thompson shell 中一样,但这次通过不同的机制)。echo
foo
脚本也被称为外壳程序(请注意,Bourne shell 还没有函数):
2.0 Shell procedures The shell may be used to read and execute commands contained in a file. For example, sh file [ args ] calls the shell to read commands from file. Such a file is called a command procedure or shell procedure. Arguments may be supplied with the call and are referred to in file using the positional parameters $1, $2...
函数首次在 80 年代初在 Korn shell(基于 Bourne shell)中引入
function foo {
...
}
句法。
后来,在 SysVR2(1984)中,函数也被添加到了 Bourne shell 中,并且使用了不同的语法:
foo() any-command
(但是当它是一个简单命令并且具有重定向时,会出现意外的行为,这可能就是 POSIX 只需要POSIX 识别复合命令(如最常用的命令)的any-command
原因)。{ ...; }
sh
在 Korn 和 Bourne shell 中,$0
函数中仍然是脚本的名称,而不是函数名称(而$1
,$2
位置参数指的是函数参数)。
这改变了函数定义的风格,其中ksh93
成为函数中的函数名称。function f {
$0
在 中zsh
,$0
是函数名称,如 ksh93 中所示。zsh
还引入了匿名函数:
function { echo $1, $2; } foo bar
或者
(){ echo $1, $2; } foo bar
语法,其中$0
成为(或在/模拟中时(anon)
保留脚本名称)。set +o functionargzero
sh
ksh
在 中zsh
,与在 中一样csh
,脚本的参数也在数组中$argv
,这消除了与参数命名类似的程序名称的混乱。
在那里,您可以使用以下方法为位置参数赋值:
argv[1]=value
或者
1=value
(也0=newprogramname
可以更改程序名称)。
而在其他类似 Bourne 的 shell 中,您需要使用set
一次分配所有内容:
set -- arg1 arg2
而你无法改变$0
。
在rc
(至少是公共域克隆)中,你不能这样做:
1 = value
但你可以这样做:
* = (arg1 arg2)
设置位置参数。你不能改变$0
in rc
,但你可以es
用0=newprogramname
like in来改变它的导数zsh
。
长话短说
, $1
...$2
引用脚本参数来自 Thompson shell,但未被调用位置参数然而。并且$0
(可能是参考argv[0]
@ikkachu 所说的)指的是脚本名称。
这位置参数该术语来自 Bourne shell。
$0
不是位置参数,因为它不引用脚本的参数。它指的是脚本的名称(argv[0]
当 shell 不执行任何脚本时,它指的是 shell 的名称;在某些 shell 中,当在函数中使用时,指的是函数名称)。
答案2
编号参数 ( $0
、$1
、 ...)argv[]
与进程启动时包含命令行参数的数组有明显的相似之处。数组的第一个元素argv[0]
通常保存进程的名称,实际参数从 开始argv[1]
。
(通常。不必如此。execve(2)
状态描述:“值在argv[0]
应该指向与正在启动的进程关联的文件名字符串”)
至少在事后,很容易想象该约定只是直接复制到 shell 中。
不过,这些值并不是直接复制的。至少在我的系统上, ./script.sh
使用 hashbang运行时启动的 shell 进程#!/bin/bash -x
获取参数/bin/bash
, -x
, ./script.sh
。也就是说,$0
脚本所看到的值位于argv[2]
shell 进程中。
我假设大多数人会认为命令名称与其参数不同,$0
所以功能上与其他不同,因此以不同的方式称呼它也不是没有道理的。
当然,我们可以有一种具有不同命名约定的脚本语言。 Perl 将程序名称放入名为 的变量中$0
,并将参数放入数组中@ARGV
,从索引零开始,即$ARGV[0]
等。
不管怎样,最明显的答案是这$0
不是一个位置参数,因为标准是这么说的:
在下面2.5 参数和变量
2.5.1 位置参数
位置参数是用一位或多位数字(除单个数字 0 之外)表示的十进制值表示的参数。
2.5.2 特殊参数
#
扩展到位置参数的十进制数。命令名称(参数 0)不应计入“#
”给出的数字中,因为它是特殊参数,而不是位置参数。
0
(零。)扩展为 shell 或 shell 脚本的名称。
答案3
$0 实际上是一个位置参数(正如下面 Michael Homer 注释中所指出的那样,POSIX 说“...将参数名称作为编号为 1 到 n 的位置参数,以及命令的名称(或者在函数的情况下)在脚本中,脚本的名称)作为编号为 0 的位置参数”。
正如人们所预料的那样,它是位置 0 的参数,代表调用命令的名称。
这非常有用,因为它允许相同的脚本根据其调用名称而有不同的行为,从而允许相同的可执行文件具有多个函数(例如,考虑符号链接)。这允许进程知道它的调用名称是什么。
如果你快速浏览一下 /bin,你会看到很多这样的例子,比如 bzcat、bzcmp、bzip2,...; mdir、mcat、mcd、mdel、...; xz、xzcat、unxz 等等。进程知道要执行什么功能的方式是通过检查位置参数 0。
注意:某些 shell 手册页可能以不同的方式处理术语:bash 的手册页说它不是位置参数,ksh 的手册页(和 POSIX 定义)说它是。无论任何术语讨论如何,在(bash 和 ksh)中,$0 都包含调用命令或脚本的名称。如果这是一个交互式 shell,$0 将包含 shell 的名称(bash、ksh...),因为它是进程名称。