shell 中有变量,如$0
、$1
、$2
、$?
等。
我尝试使用以下命令打印 shell 和环境变量:
set
但这些变量并不在列表中。
所以基本上这些变量不被认为是 shell/环境变量,对吗? (即使要输出它们,您也必须在它们前面加上$
,就像处理 shell/环境变量一样)
答案1
变量是 shell 中三种不同类型的参数之一。
- A多变的是一个参数,其名称是有效的 shell 标识符;以
_
或 字母开头,后跟零个或多个字母、数字或_
。 - 这位置性的参数是编号参数
$1
,$2
, ... - 这特别的参数都是单字符名称,除了 之外
$0
,都是各种标点符号。
set
只显示 shell 的变量。
shell 变量的一个子集是环境变量,其值要么在 shell 启动时从环境继承,要么通过export
在有效名称上设置属性来创建。
答案2
环境变量与位置参数
在开始讨论$INTEGER
变量类型之前,我们需要了解它们到底是什么以及它们与环境变量有何不同。变量例如$INTEGER
称为位置参数。 POSIX(便携式操作系统接口)标准对此进行了描述,第2.1节(强调我的):
- shell 执行函数(请参阅函数定义命令)、内置函数(请参阅特殊内置实用程序)、可执行文件或脚本,将参数名称指定为编号为 1 到 n 的位置参数,并将命令名称(或者在脚本内的函数的情况下,指定脚本的名称)指定为编号为 0 的位置参数(请参阅命令搜索和执行)。
相比之下,诸如$HOME
和 之类的变量$PATH
的变量是环境变量。它们的定义描述于标准第 8 条:
本章中定义的环境变量会影响多个实用程序、函数和应用程序的操作。还有其他环境变量仅对特定实用程序感兴趣。仅适用于单个实用程序的环境变量被定义为实用程序描述的一部分。
注意他们的描述。位置参数应该出现在命令前面,即command positional_arg_1 positional_arg_2...
。它们是由用户提供的,以告诉命令具体要做什么。当你这样做时echo 'Hello' 'World'
,它会打印出Hello
和字符串,因为这些是你想要操作的东西World
的位置参数。并且被构建为将位置参数理解为要打印的字符串(除非它们是诸如 之类的可选标志之一)。如果您使用不同的命令执行此操作,它可能无法理解and是什么,因为它可能需要一个数字。请注意,位置参数不是“继承的” - 子进程不知道父进程的位置参数,除非显式传递给子进程。通常,您会看到使用包装脚本传递位置参数 - 这些脚本可能会检查命令的现有实例或向将调用的实际命令添加其他位置参数。echo
echo
echo
-n
Hello
World
相比之下,环境变量旨在影响多个程序。他们是环境变量,因为它们是在程序本身之外设置的(更多内容见下文)。某些环境变量(例如HOME
或 )PATH
具有特定的格式、特定的含义,并且它们对每个程序都具有相同的含义。变量对于外部实用程序或您的 shell(因此对于脚本)HOME
意味着相同- 它是进程运行时用户名的主目录。/usr/bin/find
请注意,环境变量可用于解释特定的命令行为,例如UID
环境变量可用于检查脚本是否以 root 权限运行,并相应地分支到特定操作。环境变量是可继承的 - 子进程获取父进程环境的副本。也可以看看如果进程继承了父进程的环境,为什么我们需要导出?
简而言之,主要区别在于环境变量是在命令之外设置的,并且通常不会改变,而位置参数是要由命令处理并且会发生变化的东西。
不仅仅是外壳概念
我从评论中注意到,您混淆了终端和 shell,并且真的建议您阅读真实终端曾经是物理设备。如今,我们通常所说的“终端”,即黑色背景和绿色文本的窗口实际上是软件,是一个进程。终端是一个运行 shell 的程序,而 shell 也是一个程序,但它会读取您输入的内容来执行(也就是说,如果它是交互式 shell;非交互式 shell 是脚本和sh -c 'echo foo'
调用类型)。有关贝壳的更多信息这里。
这是一个重要的区别,但也很重要的是要认识到终端是一个程序,因此遵循相同的环境和位置参数规则。启动时gnome-terminal
将查看您的SHELL
环境变量,并为您生成适当的默认 shell,除非您使用-e
.假设我将默认 shell 更改为ksh
- gnome-terminal 将生成ksh
而不是bash
.这也是程序如何使用环境的示例。如果我明确告诉gnome-terminal
with-e
运行特定的 shell - 它会执行此操作,但它不会是永久性的。相比之下,环境基本上是不变的(稍后会详细介绍)。
正如您所看到的,环境变量和位置变量都是进程/命令的属性,而不仅仅是 shell。当涉及到 shell 脚本时,它们也遵循 C 编程语言设定的模型。以 C 函数为例main
,它通常看起来像
int main(int argc, char **argv)
,其中 argc
是命令行参数的数量,argv
实际上是命令行参数的数组,然后有environ
一个函数(在 Linux 上是man -e 7 environ
)来访问用户的主目录路径、PATH
我们可以在其中查找可执行文件的目录列表等。 Shell 脚本也以类似的方式建模。在 shell 术语中,我们有位置参数等$1
,而是位置参数的数量。关于什么?这是可执行文件本身的名称,它也是从 C 编程语言建模的 -$2
$#
$0
argv[0]
将是您的 C“可执行文件”的名称。这对于大多数编程和脚本语言。
交互式 shell 与非交互式 shell
我已经暗示过的一件事是两者之间的区别交互式和非交互式 shell。您输入命令的提示符 - 这是交互式的,它与用户交互。相比之下,当您有 shell 脚本或运行它时,bash -c''
它是非交互式的。
这就是区别变得重要的地方。您已经运行的 shell 是一个进程,它是使用位置参数生成的(对于bash
登录 shell 来说是一个“...参数零的第一个字符是 -,或者以 --login 选项开头的一个。”(参考) )
相比之下,使用-c
option 启动的脚本和 shell 可以利用$1
和$2
参数。例如,
$ bash -c 'echo $1; stat $2' sh 'Hello World' /etc/passwd
Hello World
File: '/etc/passwd'
Size: 2913 Blocks: 8 IO Block: 4096 regular file
Device: 801h/2049d Inode: 6035604 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2017-08-12 14:48:37.125879962 -0600
Modify: 2017-08-12 14:48:37.125879962 -0600
Change: 2017-08-12 14:48:37.137879811 -0600
Birth: -
sh
请注意,我也在那里使用过,因为-c
选项的一个小怪癖是采取第一个位置参数并将其分配给$0
,而不像通常的程序名称。
另一件需要注意的重要事情是位置参数就是我所说的“可框架”。请注意,我们首先bash
使用其自己的位置参数启动,但这些位置参数成为echo
和 的参数stat
。每个程序都以自己的方式理解它。如果我们给出stat
一个字符串Hello World
并且没有文件,Hello World
则会产生错误;bash
将其视为简单字符串,但stat
期望该字符串是现有文件名。相比之下,所有程序都会同意环境变量HOME
是一个目录(除非程序员以不合理的方式对其进行编码)。
我们可以乱搞环境变量和位置参数吗?
从技术上讲,我们可以同时处理两者,但我们不应该搞乱环境变量,而我们经常必须提供位置参数。我们可以在 shell 中运行命令并在前面添加一个变量,例如:
$ hello=world bash -c 'echo $hello'
world
export variable=value
我们还可以简单地使用shell 或脚本内的变量将变量放入环境中。或者我们可以使用完全空的环境运行命令env -c command arg1 arg2
。但是,通常不建议乱搞环境,特别是使用大写变量或覆盖已经存在的环境变量。请注意,这是推荐的,但不是标准。
对于位置参数,设置它们的方法很明显,只需将它们添加到命令中即可,但也有方法以其他方式设置它们,以及通过shift
命令更改这些参数的列表。
总而言之,两者的目的不同,但它们的存在是有原因的。我希望人们从这个答案中获得一些见解,阅读它很有趣,就像我写这个答案一样。
设置命令注意事项
根据手册,该set
命令的行为如下(来自 bash 手册,添加了强调):
没有选项,每个选项的名称和值外壳变量以可重复用作设置或重置当前设置变量的输入的格式显示。
换句话说,set
查看特定于 shell 的变量,其中一些恰好位于环境中,例如HOME
.相比之下,命令如env
和printenv
查看命令运行时使用的实际环境变量。也可以看看这。
答案3
这些$1, $2, $3, ..., ${10}, ${11}
变量称为位置参数,在 bash 手册部分中有介绍3.4.1
3.4.1 位置参数
位置参数是由一个或多个数字表示的参数,而不是单个数字 0。位置参数是在调用 shell 时从 shell 的参数中分配的,并且可以使用 set 内置命令重新分配。位置参数 N 可以引用为 ${N},或者当 N 由单个数字组成时引用为 $N。位置参数不能用赋值语句分配。 set 和 shift 内置命令用于设置和取消设置它们(请参阅 Shell 内置命令)。当执行 shell 函数时,位置参数会被临时替换(请参阅 Shell 函数)。
当展开由多于一位数字组成的位置参数时,必须将其括在大括号中。
至于$?
和$0
,这些特殊参数下一节将介绍3.4.2
3.4.2 特殊参数
shell 对几个参数进行了特殊处理。这些参数仅供参考;不允许向他们分配。
...
?
($?) 扩展到最近执行的前台管道的退出状态。
0
($0) 扩展为 shell 或 shell 脚本的名称。这是在 shell 初始化时设置的。如果使用命令文件调用 Bash(请参阅 Shell 脚本),则 $0 将设置为该文件的名称。如果 Bash 使用 -c 选项启动(请参阅调用 Bash),则 $0 将设置为要执行的字符串后面的第一个参数(如果存在)。否则,它被设置为用于调用 Bash 的文件名,如参数零给出的那样。
答案4
不,这些是脚本的参数。例如,如果您像这样调用脚本:
mynicescript.sh one two three
然后在脚本中,这些参数将可用为
$1 = one
$2 = two
$3 = three
$0 是脚本本身的名称。
因此,当您在脚本之外时,这些变量不可用($0 除外,它显示 /bin/bash - shell 本身)。