入门基础总结
Shell Shell脚本 终端
Shell
- Shell其实是一种应用程序, 用来操控计算机的内核.
- Shell分为图形化Shell和命令行Shell, 我们经常用的就是嵌套在终端里的命令行Shell.
- Shell也是一个命令解释器, 用来解释Shell脚本语言
- Shell只是一层壳, 将内核包裹在内部. 在外层接收用户命令, 通过解析命令告诉 内核 要执行哪些系统调用以及相关API操作.
Shell脚本(Shell Script)
- Shell脚本语言和我们学习过的c, python, java一样都是一种编程语言, 也拥有自己的语法
- Shell脚本是由Shell脚本语言编写而成的程序
- 不同的Shell脚本语言比如bash ,zsh等等, 可以看做不同版本的java语言, 当然也需要对应不同版本的解释器
- Shell脚本第一行要指定用哪一个版本的解释器解释该脚本, 格式
#!/bin/bash
- 我们常说的Shell编程其实是Shell脚本语言编程
- Shell脚本语言的语句结束标志
- 一行中使用分号;分隔语句
- 多行中,使用换行符分隔, 每一行代表一个语句
终端
- 早期的终端是一种硬件, 用来和计算机交换信息.
- 用户通过终端输入命令, 终端将命令传递给计算机, 计算机接受命令进行处理后将结果返还给终端, 终端将结果显示给用户.
- 后来计算机硬件一体化程度越来越高,输入输出设备完全没必要单独用一个硬件终端和计算机进行交换信息,取而代之的是使用软件终端和计算机进行信息交互, 称为终端模拟器, 也就是我们如今常见的终端.
- 终端的功能其实只有是三个: 接收输入, 显示输出, 一个GUI界面
变量
变量的分类
按照生命周期分类
- 永久变量: 不会随着shell进程的关闭而消失的变量, 永久存在. 需要在文件中设置
- 临时变量: 仅在当前的shell进程中生效, 子进程或者其余进程中均不能访问, 当前shell关闭时, 该变量失效. 用户在命令行或脚本中自定义的变量
具体说明
说明前的预备知识
-
运行一个shell脚本有三种方式:
source filename
在当前shell进程中执行, 不需要权限bash/sh filename
打开一个subshell进程执行, 不需要权限./filename
打开一个subshell进程执行, 需要可执行权限- 使用
ll
命令查看文件权限,chmod
命令修改权限- 权限分为三个类别owner, group, others, 每个类别分别由三个字符代表可读(r), 可写(w), 可执行(x)
- 更改权限的三种方法
- 在某个类别增加或减少某个权限
chmod u+x / u-x case
在owner类别增加/减少可执行权限chmod g+w / g-w case
在group类别增加/减少可写权限chmod o+r / o-r case
在others类别增加/减少可读权限
- 直接给所有类别同时增加
chmod +x / +r / +w case
给所有类别同时增加可执行/可读/可写权限
- 使用二进制的方法, 每一个类别的三个字符都相当于三个二进制位, 7表示rwx, 6表示rw-, 5表示r-x以此类推
chmod 777 case
将所有类别都增加可读可写可执行权限, 最后成为rwxrwxrwx
chmod 642 case
owner类别为rw-, group类别为r–, others类别为-w-
- 在某个类别增加或减少某个权限
- 在使用
./filename
的方法执行时, 需要先执行命令chmod u+x case
给当前用户增加可执行权限
- 权限分为三个类别owner, group, others, 每个类别分别由三个字符代表可读(r), 可写(w), 可执行(x)
- 使用
-
关于subshell子进程
- 打开两个bash程序, 在其中一个使用
ps -ef | grep bash
命令可以看到, 当前有两个bash进程
- 在其中一个bash程序中使用
./case
命令执行脚本case, (case 脚本中有read命令, 所以会等待键盘输入)
- 此时在另一个bash程序中再次运行
ps -ef | grep bash
命令, 可以发现多了一个bash子进程 (pid为81838, 这个其实是pid为59864进程的子进程, 从下图中的pid中可以看出这种关系, 也可以使用pstree -p
更直观的查看)
- 当从键盘输入并使得case脚本运行完毕退出后, 在另一个bash程序中再次运行
ps -ef | grep bash
, 发现之前的那一个pid为81838的子进程也被杀死了
- 同理,
bash filename
执行脚本也是同样的情况, 所以说这两种方式执行脚本都是开启了一个subshell子进程来执行 - 使用上述同样的验证方法, 当使用
source case
的方法执行case脚本时, 会发现并没有产生subshell, 而是在当前的bash进程中执行该脚本
- 关于Shell脚本
当你打开一个shell程序的时候, 有两种方式执行命令, 一种是直接在命令行中键入, 另一种是写脚本. 脚本其实就可以看作是一组命令, 执行脚本, 其实就是在命令行中一次执行了一组命令.
两者的区别在于, 命令行中执行命令, 肯定是在当前的shell程序中执行; 而脚本执行, 可能是在当前的shell程序中执行(source filename
), 也可能是新开了一个subshell, 在另一个shell程序中执行.
永久变量和临时变量
- 永久变量一般在/etc/profile中或者在家目录的.bash_profile中设置, 二者的区别会在之后的环境变量中说明.
在/etc/profile中设置一个永久变量, 最后一行添加export TESTCASE=‘testcse’
, 并且使用source /etc/profile
使该文件生效.
然后在case脚本文件的开头添加一行echo $TESTCASE
你会发现无论使用三种方法中的哪一种执行该脚本(无论是在subshell中执行, 还是当前shell中执行), 都会输出‘testcase’
即使你退出当前shell, 重新登录, 仍然可以使用这个变量echo $TESTCASE
上述例子说明了, 永久变量是会一直存在的, 不会因为shell进程的关闭而消失
- 临时变量只能在当前创建它的shell进程中使用, 比如在命令行中创建一变量, 在命令行中输入
TEMP=’temp‘
; 并且在case脚本中开头再添加一行echo $TEMP
当使用./case
和bash case
两种方法执行脚本的时候, 只会显示我们之前设置的永久变量$TESTCASE的值testcase, 并没有显示临时变量$TEST的值
当使用source case
执行脚本时, 会显示临时变量$TEMP的值temp
退出当前shell, 重新登录, 在命令行输入echo $TEMP
, 会发现为空, 说明之前设置的临时变量已经失效
上述例子说明了, 临时变量只能在当前shell程序中生效, 在别的shell程序中无法访问, 一旦当前shell程序关闭, 改变量也被清除.
按照作用域分类
- 系统环境变量: 对该系统中所有用户都生效. 在/etc/profile 中配置
- 用户环境变量: 只对特定的用户生效. 在家目录的.bash_profile中配置
具体说明
- “环境” 可以理解为作用域的意思
- 环境变量都属于永久变量
- 这两种环境变量都是在文件中配置, 所以都是永久变量, 即使当前shell程序关闭, 仍然存在, 再次登录的时候仍然可以使用
- 用户环境变量如果和系统环境变量重名, 用户环境变量会覆盖系统环境变量
- 不同用户之间的用户环境变量不互相访问
- 每次修改/etc/profile和.bash_profile 时, 需要使用上述执行脚本的三种方法执行这两个文件使其立即生效, 或者退出后重新登录使文件生效.
- 使用
env
命令查看所有环境变量 - 使用
set
命令查看本地变量: 当前用户在当前shell进程中可以使用的所有变量, 包含env - 一些常用的环境变量
- PATH: 存储命令的搜索路径. 每执行一个命令时, 会在该路径中搜索, 如果搜索不到会报错.
- UID: 存储当前用户的ID
- PWD: 存储当前目录的路径
- HOME: 当前用户的家目录(主工作目录)
- PS1:命令基本提示符,对于root用户是#,对于普通用户是$
- PS2:附属提示符,默认是“>“
- SHELL: 当前使用的shell类型
- HOStNAME: 主机名
变量的使用
注意事项:
- 变量的使用需要在变量前加$.
echo $PATH
- 变量的赋值语句中, 赋值符号两边没有空格.
TEMP='temp'
- 可以使用${}确定变量名范围,
echo ${TEMP}AND
如果直接写echo $TEMPAND
就会混淆 - 一般情况下变量赋值不需要加引号, 可以直接写
TEMP=temp
, 但是如果等号右边赋值的字符串中含有空格, 则需要增加引号TEMP='good bye'
因为shell是以默认以空格为分隔符 - 单引号和双引号的区别
单引号: 原封不动的输出引号内的内容.T='${TEMP}232'
会输出${TEMP}232
双引号: 会将引号内的变量转换成相应的值.T="${TEMP}232"
会输出good bye232
unset VAR
删除已经设置的变量VAR, 如果删除的是永久变量, 当前shell进程将不再能使用该变量, 但是重新登录后仍然可以使用
位置变量和特殊变量
位置变量
执行一个脚本命令时, 比如./temp.sh a b c
, 其中./temp
称为命令名, 使用位置便利那个$0引用; a, b, c称为位置参数, 使用位置变量$1, $2, $3引用
定义: shell执行用户命令时, 会把命令的第一个字段称为命令名, 后面的字段称为参数 ( 字段使用空格分隔 )
示例如下
特殊变量
shell程序自带的不可修改的变量
- $# : 传递给脚本或者函数的参数个数
- $* : 传递给脚本或者参数的所有参数
- $? : 上个命令的退出状态,或函数的返回值
- $! : 上一个后台进程的PID
- 在命令行中键入
ping www.baidu.com > ping.txt &
, 启动一个后台服务 - 然后在命令行键入
jobs -l
查看后台进程PID - 最后键入
echo $!
查看上一个后台进程的PID, 发现两者一样
- 在命令行中键入
- $$: 当前 Shell 进程的 ID; 对于 Shell 脚本,就是这些脚本所在的进程 ID.
- 打开两个bash, 在其中一个bash中输入
ps -ef | grep bash
- 在两个bash中分别输入
echo$$
, 可以知道两个bash分别对应的PID, 也就是当前Shell进程的ID
- 在case脚本中开头增加一行
echo $$
, 然后执行case脚本./case
, 在另一个bash中键入ps -ef | grep bash
, 会发现echo $$
在脚本中显示的是该脚本的PID
- 打开两个bash, 在其中一个bash中输入
read expr test命令
read命令
含义
从键盘读取输入, 赋值给变量
格式
read 变量1 变量2 …
示例
注意: 键盘输入以空格为分隔符, 回车是结束标志, 未被输入赋值的变量默认为空
expr
含义
计算四则运算的表达式
格式
expr expression
注意
- expr 只能用来计算整数
- 算数表达式中运算符两边要有空格, 与赋值符号两边没有空格做区分
- *号因为在shell中有特殊含义, 因此在表示乘时需要转义\*
- \除 在shell中是整除, 即运算结果的小数部分会直接舍弃
示例
test
含义
用来测试字符串, 整数, 文件的一些属性
格式
测试整数
表达式 | 含义 | 简写 |
---|---|---|
test int1 -eq int2 | 判断int1是否等于int2 [equal] | [ int1 -eq int2 ] |
test int1 -gt int2 | 判断int1是否大于int2 [greater than] | [ int1 -gt int2 ] |
test int1 -ge int2 | 判断int1是否大于等于int2 [greater equal] | [ int1 -ge int2 ] |
test int1 -lt int2 | 判断int1是否小于int2 [less than] | [ int1 -lt int2 ] |
test int1 -le int2 | 判断int1是否小于等于int2 [less equal] | [ int1 -le int2 ] |
test int1 -ne int2 | 判断int1是否不等于int2 [not equal] | [ int1 -ne int2 ] |
测试字符串
规定: 下面的str1和str2都是字符串字面量, 也可以使用字符串变量
表达式 | 含义 | 简写 | 备注 |
---|---|---|---|
test str1 = str2 | 判断str1是否等于str2 | [ str1 = str2 ] | 等于为真 不等于为假 |
test str1 != str2 | 判断str1是否不等于str2 | [ str1 != str2 ] | 不等于为真 等于为假 |
test str1 | 判断str1是否存在 | [ str1 ] | 存在即非空为真 不存在即空为假 |
test !str1 | 判断str1是否不存在 | [ !str1 ] | 不存在即空为真 存在即不可为假 |
test -n str1 | 判断str1长度是否不为0 [nonzero] | [ -n str1 ] | 非0为真 0为假 |
test -z str1 | 判断str1长度是否为0 [zero] | [ -z str1 ] | 0为真 非0为假 |
测试文件
Linux中一切皆文件, 目录也是一种特殊的文件, 只不过它存放的是别的文件
表达式 | 含义 | 简写 |
---|---|---|
test -d file | 判断文件file是否为目录类型 [directory] | [ -d file ] |
test -f file | 判断文件file是否为文件类型 [file] | [ -f file ] |
test -b file | 判断文件file是否为块类型 [block] | [ -f file ] |
test -c file | 判断文件file是否为字符类型 [char] | [ -f file ] |
test -x file | 判断文件file是否可执行 [excute] | [ -x file ] |
test -r file | 判断文件file是否可读 [read] | [ -r file ] |
test -w file | 判断文件file是否可写 [write] | [ -w file ] |
test -e file | 判断文件file是否存在 [exist] | [ -e file ] |
test -s file | 判断文件file是否有内容 [size] | [ -s file ] |
逻辑运算符
- -a : 逻辑与, 仅当两个条件都成立时, 结果为真
- [ exp1 -a exp2 ] (and)
- -o :逻辑或, 两个条件有一个成立时, 结果为真
- [ exp1 -o exp2 ] (or)
注意
- 简写模式前后都有空格, [ … ]
if条件语句
格式
只有if
if 条件
then
命令块
fi
或者
if 条件 ; then
命令块
fi
if … else …
if 条件
then
命令块
else
命令块
fi
或者
if 条件 ; then
命令块1
else
命令块2
fi
if … elif … elif … … else
if 条件1
then
命令块1
elif 条件2
命令块2
elif 条件3
命令块3
...
else
命令块n
fi
或者
if 条件1 ; then
命令块1
elif 条件2 ; then
命令块2
elif 条件3 ; then
命令块3
...
else
命令块n
fi
示例
只有if
if … else …
if … elif … elif … … else
注意: 这里不能以使用.
来执行, 可能是文件名带了_的缘故
学习