什么是shell
shell
英文是贝壳的意思,它是一个命令解释器,它的作用是解释执行用户输入的命令及程序等,用户每输入一条命令,Shell
就解释执行一条。这种从键盘一输入命令,就可以立即得到回应的对话方式,称为交互式。
Shell
存在于操作系统的最外层,负责与用户直接对话,把用户的输入解释给操作系统,并处理各种各样的操作系统输出结果,然后返回给用户。
输入系统用户名和密码并登录到 linux
后的所有操作都是由Shell
解释与执行的。
当命令或者程序语句不在命令行下执行,而是通过一个程序文件来执行时,该程序就被称为shell脚本
。shell
脚本本质上是命令的堆砌,但是因为命令的执行并不具有幂等性,所以需要判断来进行控制程序流程。
查看系统支持的 shell
列表,列表中的shell
又叫安全shell
,
现在各种linux
发行版默认配置的是bash
,这是由GUN
项目组开发,主要目标是与POSIX
标准保持一致。
## 查看系统支持的 shell 类型
cat /etc/shells
# /bin/sh #常用shell,linux中大部分时候是指向/bin/bash
# /bin/bash #常用shell,系统默认shell
# /sbin/nologin #常用shell,用于禁止用户登陆
# /usr/bin/sh
# /usr/bin/bash # 通常/bin/bash指向这里
# /usr/sbin/nologin
## 输出 “-bash” 表示是登录式 shell
echo $0
登录
shell
和非登陆shell
的主要区别在于启动shell
时所执行的startup
文件不同。
## 查看进程id
echo $$
## 查看系统的默认shell类型
echo $SHELL
# /bin/bash
shell解释器
shell
脚本文件的第一行,顶格给出解释器的路径 shebang
, 用于指明解释执行当前脚本的解释器。常见的解释器:#!/bin/bash
、#!/usr/bin/python
、#!/usr/bin/perl
如果脚本的第一行不给出解释器,那么就要用对应的解释器来执行脚本,这样才能保证脚本的正常执行。例如:
如果是 shell
脚本,就用 bash test.sh
执行。
如果是python
脚本,就用python
test.py`执行。
经常会有的问题是,在shell脚本中,应该选择哪种shebang呢?
#!/bin/bash
?#!/usr/bin/env bash
?还是#!/usr/bin/bash
?大部分情况下,/usr/bin/env
是优先选择的,因为它提供了灵活性,特别是你想在不同的版本下运行这个脚本;而指定具体位置的方式#! /usr/bin/bash
,在某些情况下更安全,因为它限制了代码注入的可能。
具体分析参见下方文章链接:
#!/usr/bin/env bash和#!/usr/bin/bash的比较
What is the difference between “#!/usr/bin/env bash” and “#!/usr/bin/bash”?
shell中的变量
shell
是弱类型语言,没有任何明确的类型,所有的变量都作为字符处理。
本地变量
作用域仅为当前shell
,赋值方式 name=value
, 引用 $name/${name}
,查看变量使用 set
命令,unset
撤销变量
退出当前 shell: exit
环境变量
shell
通过环境变量来确定登录用户名、命令路径、终端类型、登录目录等,所有的环境变量都是系统的全局变量,可用于所有子进程。
环境变量可以在命令中设置和创建,但用户退出命令时,这些变量的值就会丢失。
如果希望永久保存环境变量,可以配置在用户家目录下的.bash_profile
或者.bashrc
(非用户登录模式特有,例如远程 ssh
) 文件中,或者全局配置/etc/profile
或/etc/bashrc
(非用户登录模式特有,例如远程ssh
)。
环境变量赋值方式:
export name=value
相当于name=value;export name
;declare -x name=value
相当于name=value; declare -x name
;
注意:bash 中定义了很多的内部环境变量(通常为全大写字母),用于定义
bash
的环境变量。如PWD
、OLDPWD
、HISTSIZE
、HISTFILE
、HOME
[vagrant@nexus3 ~]$ echo $USER
vagrant
[vagrant@nexus3 ~]$ echo $UID
1000
[vagrant@nexus3 ~]$ echo $HOSTNAME
nexus3
查看所有环境变量的方法:
- 使用
export
不带任何参数 - 使用
declare -x
不带任何参数 env
或者printenv
就可以直接查看环境变量列表
撤销环境变量:unset evn_name
局部变量
作用域仅为代码片段(如函数上下文)
位置参数变量
为执行脚本的进程传递参数
特殊变量
shell 内置的有特殊作用的变量,例如:
$?
,返回0表示命令执行成功,1-255表示失败;$$
表示当前进程id
;$!
获取上一个在后台工作的进程的id
号!$
最近一个命令或者脚本的最后一个参数。
$ bash "version"
bash: version: 没有那个文件或目录
$ echo !$
echo "version"
version
只读变量
declare -r name
或者 readonly name
只读变量无法重新赋值,也不支持 unset
撤销,存活时间为当前 shell
进程的生命周期,只有等到当前进程终止而终止。
使用readonly -p
可以查看系统的只读环境变量;readonly -f key=value
定义只读函数;readonly -a key=value
定义只读数组变量;
shell中语句的执行顺序
- 分号分割命令,执行时采用并行执行方式,命令之间没有相互依赖关系。
COMMAND1;COMMAND2;COMMAND3;...
- 使用逻辑运算符
&&
串行执行方式,如果命令被&&
所分隔,那么命令也会一直执行下去,但是中间如果有命令出错的话,后续命名就不会执行,没错就一直执行到结束。# 一下命令之间没有相互依赖关系,顺序执行 COMMAND1 && COMMAND2 && COMMAND3...
- 使用逻辑运算符
||
执行时,采用until
执行方式,一旦遇到正确的命令,后续的命令就不再执行了。如果执行到错误的命令就是继续尝试执行后续命令,一直到遇到正确的命令或者结束为止。
第一个shell脚本
#!/bin/bash
echo "show some under /etc"
ls -d /etc/[pP]*
echo "translate lower to upper"
ls -d /var/* | tr 'a-z' 'A-Z'
echo "create temp file"
mktemp /tmp/test.XXXXXXXXXXXXXXX
shell脚本的执行
shell
脚本是从上至下,从左至右依次执行每一行的命令及语句,如果在脚本中遇到了嵌套的子脚本时,就会先执行子脚本中的内容,完成后返回父脚本继续执行。
子shell脚本的运行是通过启动一个新的进程运行实现的
- 脚本文件开头没有解释器:
bash script-name
或者sh cript-name
- 没有可执行权限的脚本:
bash script_name
或者sh cript-name
- 拥有可执行权限的脚本: 直接通过脚本绝对路径或相对路径就可以直接执行脚本了,如
path/script_name
或者./script_name
。 source script-name
或. script-name
: 这种方法通常是使用 source 或者.
(点号) 读入或加载指定的 shell 脚本到当前进程,然后执行,不会去启动新的进程。因此,使用这种方式执行脚本的时候,脚本的变量值或者函数返回值会直接留在当前进程中。这是和前面三种执行方式不同的地方。使用这种方式执行脚本的时候,如果脚本中有
exit 1
这种退出脚本的代码,当前执行脚本的shell
进程就会直接退出bash < script-name
或者cat script-name