跳转到内容

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")


Clipboard

待办事项


场景:程序崩溃并显示有关“资源暂时不可用”或“打开的文件过多”的消息。


从启动程序的 shell 中提高限制。例如,要允许每个进程打开 512 个文件

Bourne shell
$ ulimit -Sn 512
C shell
$ limit openfiles 512

Shell 参数

[编辑 | 编辑源代码]

可以通过 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%patternname%%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#patternname##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"


华夏公益教科书