Unix 指南/解释/环境
每个程序都从启动它的程序继承“环境变量”。这意味着每个程序都具有与启动它的程序相同的环境变量。程序的环境可以改变,在这种情况下,该程序后来启动的其他程序也具有更改的环境。
名为SHELL的环境变量包含默认 shell 的名称。(您可能正在运行其他 shell,但默认 shell 始终在“SHELL”中。)“SHELL”环境变量在登录时设置,并由 shell 继承。当我们启动“printenv”程序时,它继承了整个环境,包括“SHELL”,并将“SHELL”显示给我们。
在没有参数的情况下使用printenv会给出整个环境
$ printenv ... SHELL=/bin/bash ...
参见环境变量,以了解有关特定变量的信息。
每个进程对以下内容都有限制。
限制是从父进程继承的。设置限制的正常方法是更改某些 shell 的限制,然后从该 shell 启动程序。每个 shell 都提供不同的设置限制方法:Bourne shell 使用ulimit命令,而 C shell 使用limit。
以下是可用限制的列表,并给出它们的C shell 名称(“Bourne shell 选项”)。我们需要名称(“选项”)来在C(“Bourne”)shell 中显示或更改限制。
* cputime ("-t SECONDS") * filesize ("-f BLOCKS") * datasize ("-n KILOBYTES") * stacksize ("-s KILOBYTES") * coredumpsize ("-c BLOCKS") can also accept "unlimited" instead of BLOCKS * memoryuse ("-m KILOBYTES") * memorylocked ("-l KILOBYTES") * maxproc ("-p COUNT") * openfiles ("-n COUNT")
场景:程序崩溃并显示有关“资源暂时不可用”或“打开的文件过多”的消息。
从启动程序的 shell 中提高限制。例如,要允许每个进程打开 512 个文件
Bourne shell $ ulimit -Sn 512
C shell $ limit openfiles 512
可以通过 shell 的参数访问 shell 的环境。参数有三种类型
- 命名参数
- 位置参数
- 特殊参数
命名参数被称为环境变量。其中一些变量在用户登录时初始化,shell 使用它们来确定其行为。shell 还将在整个登录会话中维护某些变量,其中一些变量由 shell 定义并具有特殊含义,而另一些变量可以由用户定义。
位置参数允许引用提供给 shell 脚本的参数。shell 通过特殊参数传达进程信息。用户可以访问特殊参数,但与命名参数和位置参数不同,它们只能由 shell 本身设置。
环境变量是命名参数。变量使用命令行语句以name=value的形式分配。变量名称是字母数字字符串,可以包含数字字符或下划线,但必须以字母字符开头。下面列出了四个示例
$ n=2 $ n2=4 $ N=3 $ MYNAME=alan $ greeting="hello world"
请注意,Unix 区分大小写,因此变量“name”与“Name”不同。可以通过在变量名前加 $ 来扩展(求值)变量,例如
$ echo $greeting hello world
变量可以连接起来
$ day=21 month=01 year=2006 $ echo $day$month$year 21012006
但是,将文字与变量连接起来可能会出现问题,使用变量表示日期和月份,但对年份进行硬编码会导致
$ echo $day$month2006 05
问题是 shell 无法区分变量名month和字面值 2006。因此,它尝试扩展一个变量,该变量的变量名为month2006,该变量未设置。可以通过用大括号 {} 括起变量名来解决此问题,例如
$ echo ${day}${month}2006 05112006
虽然并不总是需要大括号,但在求值变量时使用大括号是一种很好的做法。
考虑以下示例,其中变量扩展短语用双引号括起来
$ echo "echo $greeting", displays $greeting echo hello world, displays hello world
用双引号括起来的变量仍然由 shell 扩展。要防止扩展,变量应使用单引号括起来。要按字面意思显示字符串“echo $greeting”,请使用以下语句
$ echo ’echo $greeting’, displays $greeting echo $greeting, displays hello world
shell 支持相当复杂的参数扩展。结构NAME-literal如果参数已设置,则扩展为参数的值,否则扩展为文字值。例如
$ echo ${OPSYS-unix} unix
变量OPSYS尚未分配值,因此命令的结果是在屏幕上回显字符串“unix”。如果变量被分配一个值
$ OPSYS=Linux $ echo ${OPSYS-unix} Linux
如果变量设置为 NULL,则显示空白链接而不是文字值
$ OPSYS="" $ echo ${OPSYS-unix} (blank line)
但是,如果我们使用name:-literal,则变量将求值为文字而不是其 NULL 值
$ echo ${OPSYS:-unix} unix
使用结构name=literal,如果参数尚未设置,则将参数分配为文字的值。假设变量 DAY 以前未设置
$ echo $DAY (blank line)
则以下命令行将字符串“tuesday”分配给变量DAY并将其回显到屏幕
$ echo ${DAY=tuesday} tuesday
现在变量DAY已设置,因此如果命令行用不同的文字值重复,则其值保持不变
$ echo ${DAY=wednesday} tuesday
如果DAY分配为 NULL 值,则以下参数扩展将导致返回 NULL 值
$ DAY="" $ echo ${DAY=tuesday} (blank line)
一个相关的扩展结构是name-value,它只在参数未分配时返回文字,即使值为 NULL,它也返回分配的值。
使用扩展结构name:=literal,如果参数尚未设置,则将参数分配为文字的值。例如
$ echo $DISTRO # DISTRO not assigned echo ${DISTRO:=ubuntu} ubuntu
此外,如果参数为 NULL,则分配文字,因此扩展结果为
$ DISTRO="" $ echo ${DISTO:=ubuntu} ubuntu
如果参数设置为非 NULL 值,则不会分配文字
$ echo ${DISTRO:=debian} ubuntu
扩展结构name=value类似于name:=value,但是仅在参数以前未分配时才分配文字。如果参数值为 NULL,则不会分配文字。使用name:?messagestring结构,如果参数未设置或为 NULL,则可以显示错误消息
$ echo ${DISTVER:?distribution version unknown} -bash: DISTVER: distribution version unknown $ DISTVER="" $ echo ${DISTVER:?distribution version unknown} -bash: DISTVER: distribution version unknown
如果参数分配了非 NULL 值,则不会显示error消息
$ DISTVER="hoary hedgehog" $ echo ${DISTVER:?distribution version not set} hoary hedgehog
同样,还有一个相关的结构name?messagestring,它仅在参数未设置时返回消息字符串。可以将参数扩展为其值的子字符串。扩展结构name%pattern和name%%pattern分别扩展为模式左侧的最大和最小子字符串。例如,设置变量TIME
$ TIME=10:29:39
以下命令行仅显示小时值
$ echo the hours is ${TIME%%:*} the hours is 10
而以下命令行则显示小时和分钟
$ echo the hour and minutes are ${TIME%:*} the hour and minutes are 10:29
结构name#pattern和name##pattern分别扩展为模式右侧的最大和最小子字符串。因此,以下命令行扩展为秒
$ echo the seconds are ${TIME##*:} the seconds are 39
分钟和秒可以用以下命令提取
$ echo the minutes and seconds are ${TIME#*:} the minutes and seconds are 29:39
可以使用#name结构求解字符串的长度。例如
$ echo ${#TIME} 8
变量可以是一维数组,并以name[index]的形式引用。可以使用name=(value1 value2 ... )结构分配数组,例如
DATE=($(date))
$(command)扩展为命令行上command的结果。因此,DATE被分配为命令date的输出,其中每个空格分隔的字符串都被分配给数组中的一个元素。因此,如果我们只想要年份,那么我们将扩展数组的第六个元素
$ echo ${DATE[5]} 2006
我们可以使用以下分配来更改时间信息,例如
DATE[3]=19:16:00 $ echo ${DATE[*]} Wed May 17 19:16:00 NZST 2006
如果我们需要统计已分配数组元素的数量,那么我们将使用以下表达式
$ echo ${#DATE[*]} 6
位置参数提供了一种访问 shell 脚本参数的方法。它们由数字 1、2、3、... 等引用,其中数字反映了参数在命令行中出现的顺序(从左到右)。它们不能像命名参数那样分配,也就是说,您不能这样做:1=hello。当 shell 脚本使用命令行参数调用时,位置参数被分配。但是,它们可以使用 set 命令分配
$ set $(uname -rmv)
这将导致uname -rmv的输出中空格分隔的字符串被分配给位置参数。以下语句显示了所有分配的位置参数的值
$ echo ${*} 2.6.14.4 #1 SMP Thu Dec 22 01:04:35 NZDT 2005 x86_64
以下语句给出分配的位置参数的数量(相当于 C 和 C++ 中的 argc)
$ echo $# 10
如果我们特别想要内核版本,那么我们将扩展位置参数 1
$ echo $1 2.6.14.4
两位数位置参数需要用大括号括起来。因此,如果我们想要机器的体系结构,我们不能这样做
$ echo $10 2.6.14.40
上面的表达式扩展为位置参数 1 后面跟一个文字 0。正确的方法是
$ echo ${10} x86_64
最后,位置参数 0 被设置为当前进程的名称(相当于 argv[0],在本例中应该是 shell 命令本身的名称)
$ echo $0 -bash
用户可以访问特殊参数,但只能由 shell(通常在响应某些事件时)直接赋值。我们已经遇到了参数 * 和 #,它们在命令使用参数(或使用 set 命令)调用时被设置。
?参数扩展为最后退出进程的返回值。如果我们将命令行语句括在方括号中,它将在子 shell 中执行。以下命令行会派生一个新的 shell 并立即发出 exit 命令。exit 后面的参数是子 shell 的返回值(在本例中为 255)
$ (exit 255)
父进程可以通过扩展?来访问子进程的返回值。如果进程正常终止,则返回值可能为零。但是,如果进程由于某种错误条件而终止,则如果进程设置了一个非零返回值,那么父进程就可以确定终止的原因,这将非常有用。
$ echo $? 255
shell 的进程 ID 存储在 $ 中。ps 命令显示我的 shell 的进程 ID 为 10005,这与 $ 的扩展结果一致。
$ ps | grep bash 10005 pts/77 00:00:00 bash $ echo $$ 10005
!参数计算为最后一个后台进程的进程 ID。为了演示,以下语句在后台调用 sleep 命令(睡眠 60 秒)
$ sleep 60 & [1] 24835
后台进程的进程 ID 会显示在屏幕上(在本例中为 24835)。!参数的扩展确认了这一点。
$ echo $! 24835
进程不会从其父进程继承环境变量,除非变量被导出。以下命令行显示当前进程的进程 ID,然后是 greeting 的值(之前已赋值)
$ echo ${greeting?parameter not set} hello world
如果我们启动一个新的 shell 并再次发出命令,我们会看到 greeting 在子进程中没有设置。
$ bash $ echo ${greeting?parameter not set} bash: greeting: parameter not set
终止当前 shell 进程并返回到父进程,然后导出 greeting。
$ exit $ export greeting
您可以通过键入以下内容来确认变量是否设置了 export 属性。
$ export | grep greeting declare -x greeting="hello world"
现在再次启动一个新的 shell 并评估 greeting。
$ bash $ echo ${greeting?parameter not set} hello world
新的 shell 继承了变量 greeting 并且可以对其进行评估。即使变量被重新赋值,它也会保留其 export 属性(直到 shell 终止),但可以使用 typeset 命令将其删除。
$ typeset +x greeting $ export | grep greeting # yields no result
在 Bash(以及 Kornshell,但不是 Bourne shell)中,可以在一个命令行语句中分配和导出变量。
$ export greeting="hi there" $ export | grep greeting declare -x greeting="hi there"