第六学时:进程
在 Unix下,每个程序作为一个进程运行。在 Unix中无论何时执行一行个命令,它都创建或启动一个新进程。操作系统使用一个叫做 pid或进程 ID的 5位数字 ID来跟踪进程。系统中每个进程都有一个唯一的 pid。在任何时刻,系统中都没有两个有相同 pid的进程,因为 pid是 Unix用来跟踪每个进程的。用户可以用 ps命令查看当前正在运行什么进程和系统中的所有进程。当用户启动一个进程 (运行一条命令 )时,有两种运行方式 -前台或后台。它们的区别是进程在终端怎样与用户交互。
1. 前台进程: 缺省下,用户启动的每个进程都在前台运行,进程从键盘得到输入并把输出送到屏幕上。用户虽然可以重定向进程的输入和输出,但缺省情况下输入输出与用户的终端相连接。进程在前台运行,输出被定向到屏幕。
2. 后台进程: 一个后台进程运行时不必与键盘连接。如果后台进程要求键盘输入,则进行等待。在后台运行一个进程的优点是用户可以运行其他命令,用户不需要等待一个进程完成再启动另一个。启动一个后台进程最简单的方式是在命令的后面添加一个 & 符号!
显示正在运行的所有进程的命令: ps (process status)
第二部分 Shell 编程
变量的定义方法: name=value 其中 name是变量名, value是该变量所保持的值。
变量名只能以字母或下划线开头。以数字开头的变量名将保留为 Shell使用,用户可以使用存储在这些变量中的值,但不能自己设置。在变量中不能使用如 ”!,*,-”这些字符,因为这些字符对 Shell而言有特殊的意义。变量值: Shell使程序员可存储任何想让变量存储的值。如 FRUIT=peach;FRUIT=2apples;FRUIT=apple+pear+kiwi;
需要注意的是在使用带空格的值时,需要引用改值: FRUIT=”apple orange plum”
访问值:为访问存储在变量中的值,只要在其变量名之前加上美元符号 ”$”即可。例如命令: $echo $FRUIT
peach
显示出变量 FRUIT所存储的值,本例中为 ”peach”。若没有使用美元符号 ”$”访问变量的值,则打印出变量的名字而不是它的值,如: $echo FRUIT FUIRT
只是简单地打印出 FRUIT,并不打印变量 FRUIT的值。注意美元符号 ”$” 只能用来访问变量的值,而不能用于定义变量!!牢记,当美元符号 ”$” 在一个变量名之前时,用该变量的值替换该变量!!
数组提供了一种将变量集合分组的方法。程序员不是为每个所要求的变量创建一个新名字,而是使用一个数组变量存储所有其他变量。创建数组变量的最简单方法就是为数组中的每一个元素赋值,表达式如下:
name[index]=value,这里 name为数组名, index是数组中程序员想设置的下标, value是想为该项赋的值。例如,下列命令:
$FRUIT[0]=apple
$FRUIT[1]=banana
$FRUIT[2]=orange
也可以不按照顺序赋值,如 $FRUIT[10]=plum 需要注意的是 Shell并不为下标 2~10之间的一串空白数组项用空格填满,相反, Shell只跟踪那些附了值的数组元素。
若定义的数组变量与某个已定义的标量同名,则标量值为数组下标为 0 的那个元素的值。如,若执行如下命令:
$FRUIT=apple
$FRUIT[1]=peach
元素 FRUIT 中有值 apple 。运行到这里,任何对标量 FRUIT 的访问都被看成对项 FRUIT[0] 的访问。
数组初始化的第二种方式是一次设置多个元素: name=(value1,…,valuen)
这里, name是数组名; value,从 1~n,是准备赋给每一项的值,从 0开始的连续数组下标。如 :$band=(derri terry mike gene),它等价于如下命令:
$band[0]=derri
$band[1]=terry
$band[2]=mike
$band[3]=gene
访问数组值:
在为数组赋值后,可对它们进行访问:
${name[index]}
这里 name为数组名, index是欲访问元素的下标。可使用如下方式访问数组中的所有项: ${name[*]} ${name[@]}
若数组中某一项所存储的值带有空格,则这种格式的数组访问方法就不能正常工作了,这时就需要使用第二种格式。第二种格式用引号引住所有数组项,这样嵌入的空格就保留了下来。
只读变量:通过使用 readonly命令, Shell提供了一种将变量标记为只读的方式,当命令被标记为只读时,它的值不可改变。
$fruit=kiwi
$readonly fruit
$echo $fruit
Kiwi
$fruit=cantaloupe
//errir message…….
删除变量:删除 (Unsetting) 变量告诉 Shell 从其跟踪的变量清单中移走该变量,这类似于要求 Shell 忘掉一块不常用的信息。标量和数组变量都可以通过使用 unset 命令进行删除。不能使用 unset 命令删除被标识为只读的变量。
环境变量: 当 Shell在运行时,存在三种主要变量:局部变量,环境变量, Shell变量。
局部变量 (local variable) 是只在当前 Shell实例中存在的变量,而由其他 Shell启动的程序不能使用该变量。前面所提到的变量都是局部变量。
环境变量 (environment variable) 是 Shell的任何子进程都能使用的变量,许多程序要正确运行都需要使用环境变量。通常 Shell脚本只定义程序运行时所需要的环境变量。
Shell 变量 (Shell variable) 是 Shell设置的特殊变量,是 Shell在初始化和使用过程中设置的变量,用户可以改变这些变量的值。也是 Shell正确运行所必须的,这些变量有些是环境变量而有些是局部变量。
比较局部、环境、 Shell变量
属性 | 局部变量 | 环境变量 | Shell变量 |
可被子进程访问 | No | Yes | Yes |
由用户设置 | Yes | Yes | No |
由 Shell设置 | No | No | Yes |
用户可更改 | Yes | Yes | No |
Shell要求使用 | No | No | Yes |
我们在 Linux下安装系统软件的时候 ,经常遇到一些系统环境变量配置的问题。什么是环境变量?如何定制环境变量?我将在下面做一些介绍。
一、 什么是环境变量?
Linux 是一个多用户的操作系统。多用户意味着每个用户登录系统后,都有自己专用的运行环境。而这个环境是由一组变量所定义 , 这组变量被称为环境变量。用户可以对自己的环境变量进行修改以达到对环境的要求。
二、定制环境变量
环境变量是和 Shell紧密相关的,它是通过 Shell命令来设置的。环境变量又可以被所有当前用户所运行的程序所使用。对于 bash来说,可以通过变量名来访问相应的环境变量。
下 面通过几个实例来说明
1. 显示环境变量 HOME
$ echo $HOME
/home/admin
2. 设置一个新的环境变量 NAME
$ export NAME=" RaidCheng"
$ echo $NAME
RaidCheng
3. 使用 env 命令显示所有的环境变量
$ env
HOSTNAME=test
TERM=vt100
SHELL=/bin/bash
HISTSIZE=1000
SSH_CLIENT=202.xxx.xxx.xxx 53694 22
CATALINA_BASE=/usr/local/jakarta-tomcat
SSH_TTY=/dev/pts/0
ANT_HOME=/usr/local/ant
JAVA_OPTS=-server
USER=admin
...
4. 使用 set 命令显示所有本地定义的 Shell 变量
$ set
BASH=/bin/bash
BASH_VERSINFO=([0]=" 2" [1]=" 05b" [2]=" 0" [3]=" 1" [4]=" release" [5]=" i386-redhat-linux-gnu" )
BASH_VERSION=' 2.05b.0(1)-release'
CATALINA_BASE=/usr/local/jakarta-tomcat
CATALINA_HOME=/usr/local/jakarta-tomcat
...
5. 使用 unset 命令来清除环境变量
$ export NAME=" RaidCheng"
$ echo $NAME
RaidCheng
$ unset NAME
$ echo $NAME
6. 使用 readonly 命令设置只读变量
$ export NAME=" RaidCheng"
$ readonly NAME
$ unset NAME
-bash: unset: NAME: cannot unset: readonly variable
$ NAME=" New" # 会发现此也变量不能被修改
-bash: TEST: readonly variable
三、常见的环境变量
PATH 决定了 shell 将到哪些目录中寻找命令或程序
HOME 当前用户主目录
HISTSIZE 历史记录数
LOGNAME 当前用户的登录名
HOSTNAME 指主机的名称
SHELL 前用户 Shell 类型
LANGUGE 语言相关的环境变量,多语言可以修改此环境变量
MAIL 当前用户的邮件存放目录
PS1 基本提示符, 对于 root 用户是 # ,对于普通用户是 $
PS2 附属提示符,默认是 “>”
四、通过 C 程序来访问和设置环境变 量
对于 C程序的用户来说,可以使用下列三个函数来设置或访问一个环境变量。
getenv()访问一个环境变量。输入参数是需 要访问的变量名字,返回值是一个字符串。如果所访问的环境变量不存在,则会返回 NULL
setenv()在程序里面设置某个环境变量的函 数
unsetenv()清除某个特定的环境变量的函数
另外,还有一个指针变量 environ,它指向的是包含所有的环境 变量的一个列表。下面的程序可以打印出当前运行环境里面的所有环境变量:
#include
extern char**environ;
int main ()
{
char**var;
for (var =environ;*var !=NULL;++var)
printf (" %s /n " ,*var);
return 0;
}
五、 环境变量文件
通过修改一些相关的环境定义文件来修改环境变量,比如对于 RedHat,与环境相关的文件有 /etc/profile和 ~ /.bash_profile等。修改完毕后重新登录一次或运行命令 source xxx就生效了。
标量 (scalar variable):标量一次只能存放一个值。
第八学时 替换
当遇到包含一个或更多特殊字符的表达式时, Shell执行替换。使用特殊字符 $访问变量的值,就是替换。检索变量值的过程称为变量替换 (variable substitution),除了这种类型的替换, Shell可以执行几种其他类型的替换:
文件替换 (称为 globbing)。
基于值的变量替换。
命令替换。
算术替换。
1. 文件名替换: 最常见通用的替换类型是文件名替换,有时也称为 globbing.文件名替换时 Shell用于将一包含通配符的字符串扩展成一个文件清单的过程。
用于文件名替换的通配符
通配符 | 描述 |
* | 匹配零个或多个存在的任何字符 |
? | 匹配一个存在的任何字符 |
[characters] | 匹配任何给出字符 characters 之一 |
1) 使用 *通配符:文件名替换最简单的格式是 ”*”字符。 ”*”告诉 Shell匹配零个或多个存在的字符,若只给出 *号,则匹配所有的文件。如: $ls * 列出当前目录中的每个文件和每个目录。在许多情况下要求只使用 *字符,不过,它的强大之处在于可以使用它匹配文件前缀、后缀或者同时匹配前缀和后缀。 (a) 匹配文件前缀: command prefix*;command是命令名,如 ls; prefix是欲匹配的文件名前缀。 (b) 匹配文件后缀: command *suffix; 如 $ls *doc (c)匹配后缀和前缀: command profix*suffix $ls backup*doc or $ls CGI*st*java (匹配以字符串 CGI开头,以 java结尾且包含字符串 st的文件。 ) 需要注意的是使用 * 通配符时,默认情况下是大小写敏感的,因此,前后缀的大小写指定正确是很重要的,如命令 $ls ch1* 会产生如下文件清单: ch10-01 ch10-02 …..
而命令 $ls CH1* 并不会产生相同的文件清单。
2) 使用?通配符:通配符 *的一个限制是,它每次匹配一个或多个字符。但是假设用户想列出所有文件名格式为 ch0x.doc的文件,这里 x 是单个数字或字母 。此时,就要使用通配符?。为了只匹配一个字符, Shell提供了通配符?,可使用该通配符重写命令: $ls ch0?.doc
3) 匹配字符集:通配符 *和?的两个潜在的问题是 (a) 它们匹配所有的字符,包括特殊字符如连字符 -或下划线 _.(b) 当只想匹配字母或只想匹配数字时非常困难。
有时候要对想要匹配的确切字母做更多的控制,考虑这样一种情况:用户想要匹配的文件其文件名格式为 ch0X,这里 X为一个从 0-9的数字,这时 *和?都不能完成该工作。幸运的是, Shell提供了使用 []通配符匹配字符集的能力,使用该通配符的语法如下: command[characters],这里 command是命令名,如 ls;characters代表想匹配的字符集,如下列命令完成了前面的要求: $ls ch0[0123456789].doc or $ls ch0[0-9].doc
例子:匹配所有以字母开头的文件: $ls [a-z A-Z]*
匹配所有以字母或数字结尾的文件: $ls *[a-z A-Z 0-9]
否定一个集合:
用户需要除包含字母 a的文件外的所有文件的清单,可以有两种方法来解决这个问题。 1) 指出想要文件名包含的所有字符 2) 指出不应包含字母 a的文件名。
通常采用第二种方法,通配符 []提供了匹配除在集合中指定字符外的所有字符的能力,这称为否定集。用户可通过指定!操作作为集合的第一个字符完成该任务。语法格式为: command [!characters] 这里, command是命令名,如 ls; characters是不想匹配的字符集。如,列出所有除以 a字符开头的所有文件,可以使用命令:
$ls [!a]*
2. 变量替换:
变量替换使得 Shell程序员可以基于变量的状态来操纵变量的值,变量替换有两类: a) 变量有值时动作发生 b)变量无值时动作发生;变量替换动作的范围很大,从进行一次值替换到异常中止脚本。这些动作的类型可以按变量替换的格式分为四种。对于有些必须要写成 if-then-else格式的语句,可以使用变量替换将它们写成缩写格式。
变量替换
格式 | 描述 |
${parameter:-word} | 若 parameter 为空或未设置,则用 word 代替 parameter 进行替换, parameter 的值不变。 |
${parameter:=word} | 若 parameter 为空或未设置,则 parameter 设为值 word |
${parameter:?message} | 若 parameter 为空或未设置,则 message 作为标准错误打印出来,这可用来检查变量是否正确设置 |
${parameter:+word} | 若 parameter 设置了,则用 word 代替 parameter 进行替换, parameter 的值不变 |
替换缺省值:第一种格式在变量未被设置或为空时,替换缺省值,正式的描述为: ${parameter:-word}
这里 parameter为变量名, word为缺省值。例子:
PSI=${parameter:-localhost} “$”; export PSI;
可以将它用在用户的 .profile文件以确保提示符正确设置,这种格式的变量替换不影响变量的值,它只当变量为设置时执行替换。
赋予一个缺省值:要为变量赋值,就要用到第二种格式的变量替换,这种格式正式的描述为: ${parameter:=word}
这里, parameter是变量名, word是将为该变量所设的缺省值 (如果该变量被删除了的话 )。设有命令: PSI=${HOST:= ‘uname -n’}; export PSI HOST;
在执行完该语句后, HOST和 PSI都被赋值。该例也验证了这样一个事实:所使用的缺省字符串不一定是一个固定的字符串,它也可以是一个命令的输出结果。若该替换在 Shell中不存在,与其功能相同的语句应写为:
If [-z “$HOST”];then HOST= ‘uname -n’;fi;PSI=”$HOST$”; export PSI HOST;
显然,变量替换格式比明确格式要简短清晰一些。
由于变量错误而导致中止:有时,替换缺省值会隐藏一些问题,不过, sh支持第三种格式的变量替换,它使得当变量被删除时向标准错误输出写一条信息。该格式的正式描述为: ${parameter:?message} 该格式常用于 Shell脚本以及 Shell函数中 (为正确执行,该函数要求其内的变量已被设置 )。例如,若变量 $HOME未设置或被删除,下面这条命令将退出:
:${HOME:? “Your home directory is undefined.”}
最后一种变量替换格式用于当变量已赋值时的替换:格式为: ${parameter:+word} 这里 parameter为变量名, word是当变量已设置时将替换的值,这种格式并不改变变量的值,它只改变它所替换的值。例如,一个常用于表明脚本运行于跟踪调试模式的命令是: echo ${DEBUG:+ “Debug is active.”}
3. 命令和算术替换:
Shell提供的另外两种替换是:命令替换 (command substitution) 使得可以捕获一个命令的输出而在另一个命令中替换它。算术替换 (arithmetic substitution) 使得可以使用 Shell执行一些简单的整数计算。
命令替换 常用于将一个命令的输出赋予一个变量,下面的每个例子都示范了命令替换。注意:命令替换使用的是后引号“ `” .
DATE=`date` (date命令的输出作为变量 DATE的值 )
USERS=`who | wc ` (管道的输出作为变量 USERS的值 )
算术替换, Shell可执行整数算术运算,当给出如下命令格式时执行算术运算:
$((expression)) expression的计算是根据标注算术管理进行的,下标给出了可以使用的操作符,操作符按优先级列出:
算术替换操作符
操作符 | 描述 |
/ | 除操作符,两个数相除返回结果 |
* | 乘操作符,两个数相乘返回结果 |
- | 减操作符,两个数相减返回结果 |
+ | 加操作符,两个数相加返回结果 |
() | 括号内的表达式应在其他运算前运算
|