交互式 Shell 和 非交互式 Shell
- 交互式程序是指与人进行沟通的程序;其输入来源于键盘或鼠标,输出发送到显示器;
- 非交互式程序是指独立于人运行的程序;一般情况下,从文件中获取输入,并将输出写入到另一个文件中;
shell 即是交互式程序,也是非交互式程序。当充当用户界面时,即 CLI(命令行界面),是交互式shell;当作为脚本解释器时,运行shell脚本时,是非交互式shell。当启动一个shell脚本时,会自动启动一个新的shell,并将解释脚本的任务交给该shell处理,这个新shell被设置为非交互式的,当任务完成时,这个非交互式shell也就终止了。
shell 是交互式的还是非交互式的,取决于启动它时为其指定的选项。
环境、进程和变量
在 Unix 系统中,每个对象都被表示为一个文件或者进程。文件存放数据或者用来访问资源,而进程是正在执行的程序。
在进程运行过程中,它需要访问所谓的环境,即一组用来存放信息的变量。
变量的定义:变量是一个用来存储数据的实体。每个变量都有一个名称和一个值。其中变量名是用来引用变量的标识符,值是存储在变量中的数据。
变量名的两个规则:
- 首先,变量名必须由大写字母(A-Z)、小写字母(a-z)、数字(0-9)或者下划线字符(_)构成;
- 其次,变量名的一个字符必须是字母或者下划线,不能是数字。
当使用 Unix shell 时,有两种不同类型的变量:shell变量 和 环境变量。
变量只有4个操作:创建变量、查看变量的值、修改变量的值 以及 销毁变量。
对于大多数变成语言来说,变量可以包含许多不同的类型的数据,但是对于 shell 来说,变量几乎总是存储一种类型的数据,即 字符串。
在创建变量时,尽管不是必须的,但是还是通常会给它赋一个值,如果变量没有赋值,那么这个变量拥有一个 null 值,这意味着这个变量没有值。
变量、环境和进程如何结合在一起的:例如在 shell 提示出启动了 vi 文本编辑器。用技术术语讲,就是一个进程(shell)启动了另一个进程(vi)。当这种情况发生时,第一个进程称为父进程或者双亲,第二个进程称为子进程或者孩子。
在子进程创建时,系统为子进程赋值了父进程的环境;也就是子进程继承了父进程的环境;父进程可以访问的全部环境变量,其子进程也可以访问。
环境变量和shell变量
在程序中,局部变量只存在于创建它的范围内。而全局变量可以在程序的任何地方使用。
当使用 Unix shell 时,shell 中的全局变量和局部变量与程序员使用的全局变量和局部变量是相似的。所有的 shell 都使用全局变量和局部变量。
shell 中有环境变量,因为环境变量对所有进程可用,所以它们是全局变量,实际上,通常称环境变量为全局变量。其次,有的 shell 变量只在特定的 shell 中使用,并不属于环境,因此这些变量不从父进程传递给子进程,基于这一原因,称这种变量为局部变量。
通常,局部(shell)变量以两种方式使用:
- 可以用来存放对 shell 本身有意义的信息,例如 ignoreeof 变量
- shell 变量在 shell 脚本中以普通程序中局部变量的使用方式使用:作为临时存储容器;
shell 变量是创建它们的 shell 的局部变量。环境变量是全局变量,因为使用相同环境的任何进程都可以访问它们。在 shell 中,局部变量和全局变量之间的界限是模糊的。
在一些编程语言中,有一个传统,全局变量通常采用大写字母的名称,而局部变量采用小写字母的名称。C-Shell 家族采用这种传统;但是 Bourne shell 家族有所不同:shell 变量和环境变量传统上都采用大写字母的名称。
对于大多数编程语言来说,变量或是是局部的,或者是全局的。而对于 shell 来说,有一个奇怪的问题:一些变量同时拥有局部变量和全局变量的含义。Bourne shell 中,每个新变量都被设置为 shell变量,如果需要使某个变量同时成为环境变量,则需要使用 export 命令,即将变量导出到环境中。
例如,定义变量:
shell
HARLEY=cool #注意定义变量时,两边不能有空格
此时,HARLEY 只是一个 shell 变量。如果启动一个新的 shell或是命令,则新进程不能访问 HARYLEY,这个变量。下面将 HARLEY 这个变量导出到环境中:
shell
export HARLEY
此时 HARLEY 变量即是 shell 变量又是环境变量了。
export 后的环境变量生命周期只到当前 Shell 会话结束,并且只能被该会话启动的子进程继承和访问。
在 C-Shell 家族中,环境变量通过命令 setenv 创建,并且以大写字母命名,shell变量由 set 命令创建,并且以小写字母命名。对于一些特殊变量,需要即是局部变量又是全局变量,C-Shell 的解决方法就是定义少数几个特殊的 shell变量,并将它们绑定到对应的环境变量中,并当这些变量改变时,shell会自动地更新对应的变量。
下面是C-Shell家族中常用的几个变量和其对应关系(由于只适用 C-Shell 家族,所以不用特别关注):
| shell 变量 | 环境变量 | 含义 |
|---|---|---|
| cwd(current/working directory) | PWD(print working directory) | 当前工作目录 |
| home | HOME | home 目录 |
| path | PATH | 搜索程序的目录 |
| term | TERM | 正在使用的终端类型 |
| user | USER | 当前用户标识 |
显示环境变量:env、printenv
显示默认变量,可以使用命令 env,在许多系统上,还有另一个命令可以使用:printenv
shell
env | less #使用 less 命令每次一屏地显示输出
printenv | sort | less # 使用 sort 命令使输出根据字母表顺序排序(这种结构叫做管道线)
在 bash 中,这两个命令都可以使用。
以下是常用的环境变量列表:
| 变量 | 含义 | Ubuntu Bash 输出(登录为 singer) |
|---|---|---|
| CDPATH | cd 命令搜索的目录 | null |
| COLUMNS | 屏幕或者窗口的宽度(以字符为单位) | 80 |
| EDITOR | 默认文本编辑器 | null |
| ENV | 环境文件的名称 | null |
| FCEDIT | 历史列表:fc 命令使用的编辑器 | null |
| HISTFILE | 历史列表:用来存储历史命令的文件名称 | /home/singer/.bash_history |
| HISTSIZE | 历史列表:存储历史命令的最大数量 | 1000 |
| HOME | home 目录 | /home/singer |
| HOST | 计算机名称 | null |
| HOSTNAME | 计算机名称 | singer-VirtualBox |
| HOSTTYPE | 主机计算机的类型 | x86_64 |
| IGNOREEOF | 在结束 shell 之前忽略的 eof 信号(^D)的数量 | null |
| LOGNAME | 当前用户标识 | singer |
| MACHTYPE | 系统描述 | x86_64-pc-linux-gnu |
| 查看新邮件的文件 | null | |
| MAILCHECK | shell 查看新邮件的时间间隔(单位为秒) | 60 |
| MAILPAT | 查看新邮件的文件 | null |
| OLDPWD | 前一个工作目录 | null |
| OSTYPE | 操作系统的描述 | linux-gnu |
| PAGER | 显示数据的默认程序(应该是 less) | null |
| PATH | 搜索程序的目录 | /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/snap/bin |
| PS1 | shell 提示(通过修改这个变量进行 shell 提示的定制) | ... |
| PS2 | 连续行的特殊 shell 提示 | > |
| PWD | 当前工作目录 | /home/singer |
| RANDOM | 0 至 32767 之间的随机数 | 0 至 32767 之间的随机数 |
| SECONDS | 自 shell 激活之后过去的时间(单位为秒) | 2852 |
| SHELL | 登录 shell 的路径名 | /bin/shell |
| TERM | 正在使用的终端类型 | xterm-256color |
| TMOUT | 在不键入命令时,系统自动注销前的等待时间(单位为秒) | null |
| TZ | 时区信息 | null |
| USER | 当前用户标识 | singer |
| VISUAL | 默认文本编辑器(覆盖 EDITOR) | null |
显示 shell 变量:set
使用不带选项或者参数的 set 命令可以显示所有的 shell 变量以及它们的值:
shell
set
此命令适用于所有的 shell。但是注意,对于 C-Shell 家族,shell 变量都是小写字母的名称;对于 Bourne Shell 家族,shell 变量都是大写字母的名字,因此只看变量的名称无法确定跟这个变量是 shell变量还是 环境变量。确定哪些 Bourne Shell 变量没有导出的唯一方法就是比较 set 命令和 env 命令的输出。
C-Shell 家族使用 shell 变量控制 shell 的行为;Bourne Shell 家族使用 shell 选项来控制 shell 的行为。
显示及使用变量的值:echo、print
- 使用 env 或者 printenv 命令显示所有环境变量的值;
- 使用 set 命令可以显示所有的 shell 变量;
而显示一个变量的值,可以使用 echo 命令和(美元符号),后面跟着花括号括起来的变量名 {NAME}:
shell
echo ${TERM}
echo $TERM # 没有歧义时,还可以省略花括号
#可以使用 echo 命令以自己希望的任意方式显示变量和文本:
echo The terminal type is $TERM.
# echo 同时显示不止一个变量:
echo $HOME $TERM $PATH $SHELL
# 某些时候必须使用花括号:
echo "My favorite sport is ${ACTIVITY}ing."
在 Ubuntu 默认 bash 中能使用 echo 命令,没有 print 命令。print 命令由 korn shell 引入。
元字符
在 shell 中,一些标点符号字符称为"元字符",它们拥有特殊的含义,为了防止 shell 解释元字符,需要将元字符包含在双引号中。这就告诉 shell 照字面意义接受字符。
shell
echo The terminal type is <$TERM>.
但是,由于 < 和 > 字符是元字符,表示"重定向",所以上述的命令不能正常运行。需要使用如下命令:
shell
echo "The terminal type is <$TERM>"
#输出:The terminal type is <xterm-256color>
Bourne shell 家族使用变量:export、unset
创建变量:
NAME=value(变量的值必须是字符串)(不要在等号两边添加空格)
shell
HARLEY=cool
WEEDLY="a cool cat"
# 如果变量存在,使用相同的语法修改变量的值
HARLEY=smart
导出到环境变量中:
在 Bourne Shell 家族中,每个新变量都被设置为 shell 变量,使用 export 命令将命令导出到环境变量中:
shell
export HARLEY WEEDLY # 导出到环境变量
PAGER=less; export PAGER # 在一行中创建变量后立刻导出
export 命令实际上允许同时设置变量并导出到环境中,语法为:
shell
export NAME[=value]...
export PAGER=less
export HARLEY WEEDLY LITTLENIPPER # 一次性导出过个变量
export PAGER=less EDITOR=vi PATH="/usr/local/bin:/usr/bin:/bin" #一次性定义并导出到环境变量
删除(复位 unset)变量
语法为:unset NAME...
shell
unset HARLEY WEEDLY
C-Shell家族使用变量:setenv、unsetenv、set、unset
由于 C-Shell 家族基本用不上,就不详述:
shell
# 设置(创建)环境变量
setenv PATH /usr/local/bin:/usr/bin:/bin
setenv HARLEY cool
setenv WEEDLY "a cool cat"
setenv LITTLENIPPER # 未指定其值,默认为 null
# 复位(删除)环境变量
unsetenv HARLEY
# 创建 shell 变量
set term=vt100
set path=(/usr/bin /bin /usr/ucb) #圆括号将一组字符串括起来
set ignoreeof #创建值为 null 的变量,或设置 ignoreeof 的值为 null
# 删除 shell 变量
unset ignoreeof
unset harley
set harley=cool # 创建一个名为 harley 的 shell变量,指定其值为 cool
set harley # 将变量 harley 的值设置为 null
unset harley # 完全删除变量 harley
shell 选项: set -o、set +o
对于 Bourne Shell 家族来说,使用 shell 选项来控制 shell 行为的各个方面。shell 选项就像 on/off 开关一样。当打开一个选项时,就说设置了这个选项,这将告诉 shell 以某种方式运行。当关闭这个选项时,就所复位了这个选项,也就是告诉 shell 停止以这种方式运行。
shell 选项或者是 off 或者是 on,它们不需要创建。例如,shell 支持一个叫做 "作业控制" 的功能,允许在后台运行程序。打开作业控制需要设置 monitor 选项。
shell 选项设置或是复位的方式有两种:
第一种,当 shell 启动时,可以以普通的方式指定选项,例如:
shell
ksh -m # 启动一个 korn shell,并设置(打开)monitor 选项
第二种使用 set 命令的一种变体:
shell
set -o option # 设置一个选项,option 是选项的长名称
set +o option # 复位一个选项
set -o monitor # 设置 monitor 选项
set +o monitor # 复位 monitor 选项
set -o ignoreeof # 设置 ignoreeof 选项
set +o ignoreeof # 复位 ignoreeof 选项
shell 每次启动时,根据 shell 似乎交互式还是非交互式的,各种选项会被默认设置或复位,大多数情况下,默认 shell 选项就可以满足要求,极少需要修改 shell 选项。
shell 选项的列表就不列出了。
显示 shell 选项:shopt
在 Bourne shell 家族中,要显示 shell 选项的当前值,可以使用 set -o 或者 set +o 命令本身:
shell
set -o # 以一种容易阅读的方式显示所有 shell 选项的当前状态,人类可读
set +o # 以一种紧缩的格式显示相同的信息,适合用做 shell 脚本,机器可读
而在 Bash 中,有一个特殊的命令 shopt(shell options,shell 选项)可以显示更多的 shell 选项:
shell
shopt
一般来说,需要关注的选项只有:ignoreeof、monitor 和 noclobber、以及 emacs 或者 vi。
| 选项 | 长名称 | 含义 |
|---|---|---|
| -I | ignoreeof | 忽略 eof 信号 ^D;使用 exit 退出 shell |
| -m | monitor | 作业控制:启用 |
| -C | noclobber | 不允许重定向的输出替换某个文件 |
| -E | emacs | 命令行编辑器:Emacs 模式,关闭 vi 模式 |
| -V | vi | 命令行编辑器:vi 模式,关闭 Emacs 模式 |