[TOC]
意识 Shell
什么是 Shell
Shell 是命令解释器, 将命令解释给内核来执行, 它是用户和内核之间的中间层.
用户
????
Shell
????
内核
查看所有的 shell 类型
cat /etc/shells
/bin/sh
/bin/bash # 基于 bsh 的增强重构版, 是 CentOS 7 和 Ubuntu 的默认 Shell
/usr/bin/sh
/usr/bin/bash
/bin/tcsh
/bin/csh
Linux 的启动过程
启动程序从上至下
BIOS 根本的输入输出零碎
|
MBR 硬盘的主疏导局部(前 446 字节是主疏导记录, 前 512 除了主疏导记录还包含磁盘分区表)
|
BootLoader(grub) 启动和疏导内核的工具, 目前应用的是 /boot/grub2
|
kernel 内核
CentOS 7 | CentOS 6
/ \
systemd init 1 号过程(在 CentOS 6 中是 /usr/sbin/init 过程, CentOS 7 则是 /usr/lib/systemd/systemd)
| |
零碎初始化 由 shell 脚本实现疏导 CentOS 7 中有一部分是由 systemd 配置, 应用程序疏导. 零碎初始化仍是由 shell 脚本实现.
|
shell
CentOS 6 在 init 前面的疏导会和 CentOS 7 有稍微的差别.
在 CentOS 6 中, init 的疏导步骤
/etc/rc.d/rc.sysinit
零碎初始化工作- 期待用户终端接入
在 CentOS 7 中, systemd 的步骤
/etc/systemd/system
读取启动级别/usr/lib/systemd/system
读取各个 service
# 导出主疏导记录
dd if=/dev/sda of=mbr.bin bs=446 count=1
hexdump -C mbr.bin # 查看主疏导记录
# 导出主疏导记录和磁盘所有分区表
dd if=/dev/sda of=mbr2.bin bs=512 count=1
hexdump -C mbr2.bin # 最初面以 55aa 结尾示意可疏导
如何编写 shell 脚本
规范的 Shell 脚本要蕴含的元素
- Sha-Bang
首行的
#!
结尾的局部文本文件首行增加
#!/bin/bash
能够在以./ 脚本.sh
这种形式执行脚本时申明以后是bash
脚本, 零碎会自行抉择对应的 shell 来执行, 若是以bash 脚本.sh
则会被视为正文. #
结尾的视为正文- 脚本执行权限, 若是二进制可执行文件只须要
x
, 若是文本文件则须要rx
- 通常约定 bash 脚本的扩大名为
.sh
- 在一行中可应用
;
分隔多条命令, 会顺次按程序一一执行命令, 只有在前一个命令执行完才会执行后一个命令.
确保脚本执行谬误时马上退出
可在脚本结尾设置: set -e
, 从而告知 bash, 若有任何语句执行失败, 就间接退出脚本, 避免谬误像滚雪球般变大.
此时
$?
无奈应用
若在 set -e
模式下为了确保某些语句失败不退出脚本, 可采纳如下形式
# 办法 1
command || {echo "command failed"; exit 1;}
# 办法 2
if ! command; then
echo "command failed"
exit 1
fi
对于 set
更多参见 set 命令
shell 脚本执行形式
执行命令的 4 种形式
-
bash file.sh
会在以后终端下产生一个 bash 子过程, 再由该子过程去执行该脚本.
这种形式无需赋予脚本执行权限
-
./file.sh
同样会在以后终端下产生一个子过程, 会依据脚本的 Sha-Bang(即第一行的
#!/path/to/bash
) 来解释该脚本.比方 python 脚本第一行是
#!/usr/bin/python
须要 赋予脚本执行权限
-
source file.sh
在以后过程执行该脚本, 脚本中的操作会影响以后环境.
这种形式同样无需赋予脚本执行权限.
因为是在以后过程执行, 因而操作会影响以后过程, eg 脚本中的
cd /tmp
同样会扭转以后环境的工作目录. . file.sh
.
是source
的缩写, 等价于source file.sh
补充
exec <command>
应用
command
过程替换以后过程, PID 不变, command 执行完后间接退出.
内建命令和外部命令的区别
内建命令
- 不须要创立子过程
- 对以后 Shell 失效
比方
cd
是内建命令, 执行时会切到以后 Shell 的工作目录
shell 选项
shopt 命令
查看 / 设置 shell 选项
shopt [选项] [< 选项名 >]
示例
shopt # 查看所有选项及其值
shopt < 选项名 > # 查看指定选项的值
选项
-s set, 设置值为 on
-u unset, 设置值为 off
局部重要选项
login_shell # 批示以后是 login shell 还是 non-login shell
实用示例
获取以后脚本所在目录
# 将当前目录保留到变量 PWD 中(留神命令)
PWD="$(cd $(dirname ${BASH_SOURCE[0]}) && pwd -P)"
BASH_SOURCE
变量是一个数组, 其第一个参数是以后脚本名
若应用
source
来执行脚本时, 参数$0
的值是父脚本的名字, 而不是以后脚本的名字.
打印消息时带日期工夫
log() {echo $(date "+%Y-%m-%d %H:%M:%S")" "$*
}
log "lala"
管道与重定向
一个过程默认会关上规范输出、规范输入、规范谬误三个文件描述符.
- 规范输出默认是由终端输出
- 规范输入和规范谬误默认是输入到终端
管道与管道符 |
管道和信号都是过程通信的形式之一.
匿名管道 (管道符 |
) 是 Shell 变成常常用到的通信工具.
管道符 |
, 将前一个命令执行的后果传递给前面的命令.
- 管道实际上是将不同的过程的规范输入和规范输出做一个连贯.
- 将前一个命令的 规范输入 连贯到下一个命令的 规范输出
-
管道符是通过创立子过程的形式来运行的
子过程如果是一个 shell, 则称之为 子 shell.
在有管道符的命令中运行内建命令其实是在新的子 shell 中执行的, 不会影响以后 shell 环境, 这个须要了解好.
因而个别防止在管道符中应用内建命令
- 如果连贯的是外部命令, 则会按程序同时建设多个子过程来别离执行外部命令, 同时按程序连贯各个规范输入和规范输出
示例
ps | cat echo 123 | ps
示例
cat
子过程的规范输入 (1) 重定向至匿名管道 (463978), 而less
的规范输出(0) 重定向至同一个匿名管道(463978), 也就是cat
的规范输入通过匿名管道连贯重定向至less
的规范输出.
留神管道符和分号的区别:
- 分号隔开的多条命令是没有任何关系的, 且每次只会执行一条, 执行完后才会持续下一条.
- 管道符隔开的多条命令是具备输入输出重定向关系的, 会简直同时启动 (理论程序是从做到右), 其中 执行的命令 (包含内建命令) 都是在新的子过程中执行, 而不是在以后 shell 中执行.
返回码
失常能够应用
$?
获取上一条命令的执行后果状态码(0 失常, 非 0 异样)然而若是在执行一条管道后应用
$?
获取的只是管道最初一个指令执行返回的状态码
$PIPESTATUS
变量相似 $?
, 但它保留的是管道中每个命令的返回码
${PIPESTATUS[0]}
示意管道中第一个命令的返回码- 若上一条命令不是管道, 同样会更新
`$PIPESTATUS
值
参考: https://www.cnblogs.com/suane…
重定向符号
重定向符号实际上是将过程的规范输出和规范输入与文件建设连贯.
- 利用文件代替终端输出
- 利用文件代替终端输入
所有重定向符号包含
-
输出重定向
<
<<EOF
<<"EOF"
不本义特殊字符<<'EOF'
不本义特殊字符
-
输入重定向
语法 [< 文件描述符 =1>]< 重定向符号 > 参数解释 < 文件描述符 > 1 规范输入(不写则默认) 2 规范谬误 & 规范输入和规范输入 < 重定向符号 > > 清空并写入 >> 追加写入 示例 &>> # 将规范输入和规范谬误重定向至文件, 并追加写入 1>1.txt 2>>2.txt # 将规范输入重定向至 1.txt 文件并清空该文件后写入. 同时将规范谬误重定向至 2.txt 文件并追加写入. # 组合应用输出和输入重定向(本义内容, 其中的变量会被替换, `whoami` 命令会被执行并替换) cat > /path/to/file <<EOF i am $USER `whoami` EOF # 组合应用输出和输入重定向(不本义内容) cat >> /var/spool/cron/root <<'EOF'
EOF
### 应用输出重定向来代替规范输出
输出重定向
read < 变量名 > <<EOF
123
EOF
从文件输出重定向
echo 123 > tmp.txt
read < 变量名 > < /tmp.txt
> 不能应用管道, 因为管道里的命令是在新的过程中执行, 读取的变量无奈影响以后环境
>
> ```sh
> # 有效
> echo "123" | read < 变量名 >
> ```
>
>
## xargs 命令
从规范输出构建并执行命令行
- 实用于待执行命令只能从参数中而不是规范输出读取值的状况
xargs [选项] <command=echo>
选项
分隔符
-d # 定义输出分隔符, 默认是空白和换行
--null, -0 # 以 null 作为分隔符, 常搭配应用(文件名可能有空格, 反斜杠等) `find -print0 | xargs -0`
宰割成多个命令并别离执行
-n <n> # 将最多 <n> 个参数用于构建一个命令行
-L <n> # 如果规范输出蕴含多行,- L 参数指定多少行作为一个命令行参数(别离执行屡次命令). 个别更罕用 -n
-I < 替换符 > # 应用 - I 指定一个替换字符串,这个字符串在 xargs 扩大时会被替换掉,当 - I 与 xargs 联合应用,每一个参数命令都会被执行一次
--interactive, -p # 一一命令确认是否执行, 只有回复 y 或 Y 结尾的才会执行, 否则略过
--verbose, -t # 在执行之前在规范谬误输入显示待执行的命令
--max-procs <n> # 最多同时运行多少个过程, 默认是 1, 如果是 0 则示意不限度. 可与 -n 配合, 防止只执行一次 exec
用法
< 前一个命令 > | xargs # 通过管道符从新构建待执行命令
xargs # 由用户手动输出(Ctrl+D 完结输出), 并构建待执行命令
示例: echo, rm, mkdir, ls 等命令
# 简略的 echo 示例
echo 123 | xargs echo
# 应用每 2 个参数执行一次命令
echo {0..9} | xargs -n 2 echo
# echo 执行了 3 次(以下几种等效)
echo -e "a\nb\nc" | xargs -L 1 echo
echo -e "a\nb\nc" | xargs -n 1 echo
echo "a b c" | xargs -n 1 echo
# 找出所有 TXT 文件当前,对每个文件搜寻一次是否蕴含字符串 abc。find . -name "*.txt" -print0 | xargs -0 grep "abc"
# 变量
## 变量定义
Shell 的变量不辨别类型
命名规定
- 字母、数字、下划线
- 不以数字结尾
## 变量赋值
### read 命令
交互方式
通过规范输出读取变量值
read [选项] 变量名
选项
-a # array assign the words read to sequential indices of the array
# variable ARRAY, starting at zero
-p <prompt> # 在读取变量前, 打印一个提醒文本(不换行)
-r # 不解析反斜杠, 即读入原始值(失常应用时举荐)
> 写 Shell 脚本时一边会防止用交互式来给变量复制, 除非是有必要.
* 继续读取管道中的数据 *
seq 1 10 | while read line; do echo $line; done;
### 非交互方式
留神: `=` 相邻的左右两侧不容许呈现空格.
** 字符串赋值 **
变量名 = 变量值
eg.
a=123
> 等号的左右不容许呈现空格
>
> 变量值蕴含空格时, 须要用 `""` 或 `''` 蕴含起来
>
> 单引号, 双引号的区别:
>
> - 单引号: 不会对变量值中的援用命令、援用变量、转义字符等进行解析。> - 双引号:会解析变量中的援用命令、援用变量、转义字符,再将解析后的值赋值给变量名
** 数学表达式赋值 **
let 变量名 = 变量值
示例
let a=10+20 # a 的值是 30
> 变量值只能是数学表达式
>
> 因为 Shell 的计算性能较差, 个别不怎么用来计算
** 将命令赋值给变量 **
变量名 =” 命令 ”
如果变量的值是可执行命令, 则可间接应用 $< 变量名 > 来执行命令.
但如果要将运行后果赋值给另一个变量, 则需配合 $ 或 `
示例
间接执行变量值
l="ls -hl"; $l # 此时等价执行了 ls -hl
将变量值执行后果赋值给另一个变量
cmd="uptime"; result=$($cmd) # 将 uptime 运行后的后果赋值给 result 变量
cmd="uptime"; result=`$cmd` # 同上
> 罕用于拼接命令后再执行
** 将命令后果赋值给变量 **
变量名 =$(命令)
变量名 =命令
示例
current=`pwd`
current=$(pwd)
## 变量的援用
- `${变量名}` 示意对变量的援用
- `echo ${变量名}` 查看变量的值
- `${变量名}` 在局部状况下能够省略为 `$ 变量名 `
> 局部清空指: 在变量名前面紧跟其余字符时
- `${! 变量名}` 对变量的援用的援用
t1=t2
t2=”i am t2″
echo $t1 # 输入 “t2”
echo ${!t1} # 输入 “i am t2”
## 变量的作用范畴
变量的作用范畴默认只在以后 Shell 中, 其父、子、平行 Shell 都是不可见的.
> 如果想要某个脚本中定义的变量在以后 Shell 失效, 则有两种办法:
>
> - 在以后 shell 中执行 `source 脚本.sh` 或 `. 脚本.sh`
> -
** 变量的导出 export**
在父过程中执行 export, 从而让子过程可能获取父过程中的变量.
export 变量名[= 变量值]
> 在子过程中对父过程的变量名批改在父过程是不感知的(有效), 然而在子孙过程是无效的.
** 删除变量 unset**
删除变量
unset 变量名
## 零碎环境变量
### 长期设置环境变量
环境变量指的是: 每个 Shell 关上都能够取得到的变量.
> 环境变量都是通过 export 的, 因而对其批改会影响到子过程.
若只是想长期批改某个环境变量来执行某个程序, 那么能够不便地相似如下所示:
< 环境变量名 >=< 环境变量值 > < 命令 >
示例
LANG=c man iptables # 查看英文版本的 man 帮忙
### 局部变量解释
#### 环境变量
** 很重要 **
PATH # 以后命令的搜寻门路, 用 : 分隔. 能够用如下形式新增命令搜寻门路
# PATH=$PATH:/path/to/bin
PS1 # 以后终端提醒文本
** 晓得即可 **
USER # 以后用户名
UID # 以后用户 id
#### 预约义变量
$? # 上一条命令是否正确执行, 0 示意正确, 1 示意有出错.
$$ # 以后过程号
$0 # 所属过程的过程名, 而不是脚本名!!! 这个概念不一样
# 如果用 bash 形式来执行脚本则该值是脚本名, 如果用 source 的形式则该值是父过程的过程名.
# 这个分割之前的脚本执行形式很容易了解
$LINENO # shell 脚本以后的行号
> `$?` 罕用于断定上一条命令是否正确执行, 从而实现脚本自动化解决异样.
#### 地位变量
$* # 脚本执行的所有参数
$@ # 脚本执行的所有参数
$# # 参数个数
$1 # 第 1 个参数
…
$9
${10} # 第 10 个参数, 此时不能省略大括号
`$*` 与 `$@` 不同之处在于用双引号括起来时行为不一样
当传入参数为 a b c 时
“$*” # “a b c”
“$@” # ‘a’ ‘b’ ‘c
### env 命令
查看以后的所有变量(包含环境变量)
env
### set 命令
批改 shell 环境运行参数
set # 显示所有环境变量和 Shell 环境参数
set [参数] [-o option-name] [arg …] # 设置 shell 环境参数
选项
-u # 应用到不存在的变量时报错 (unbound variable) 并终止脚本(默认疏忽), 等价 -o nounset
-x # 关上回显, 每个命令执行的时候会输入所执行的命令, 不便调试简单脚本(默认不关上), 等价 -o xtrace
-e # 命令运行失败时退出脚本, 避免谬误累计, 理论开发倡议关上(默认疏忽), 等价 -o xtrace
# 留神不适用于管道(除非是在管道的最初一个子命令)
+e # 实用于长期敞开 `-e`
-o pipefail # 管道中任意一个子命令失败都退出脚本(默认不会, 即便关上 -e)
罕用写法
set -euxo pipefail
set -eux -o pipefail
也能够在执行 bash 脚本时从命令行传入:
bash -euxo pipefail script.sh
`set` 局部参考: http://www.ruanyifeng.com/blog/2017/11/bash-set.html
#### set -e
开启 `set -e` 后若局部语句容许失败(或失败后须要执行其余逻辑), 则可采纳如下写法
写法 1
command || true
command || {echo “fail”;}
写法 2
if !command; then
:
fi
写法 3
set +e # 长期勾销 -e
do something
set -e # 复原 -e
### 环境变量配置文件
配置文件
- `/etc/profile`
- `/etc/profile.d/*`
- `~/.bash_profile`
- `~/.bashrc`
- `/etc/bashrc`
#### 从存储地位划分:
- `/etc/` 下的配置是所有用户通用
- `~/` 下的配置是仅集体无效
#### 从文件类型划分:
- `profile`
配置环境变量
- `bashrc`
别名及函数定义
#### 依据 login 和 no-longin shell 划分
用户在登录时分为以下两种 Shell
能够通过如下命令查看以后属于哪种
shopt login_shell # on 示意 login shell, off 示意 non-login shell
**login shell**
- 包含: `su - `
- 会加载 `profile` 和 `bashrc` 类文件.
- 加载程序如下
su – root
loading /etc/profile
loaded /etc/profile
loading ~/.bash_profile
loading ~/.bash_rc
loading /etc/bashrc
loaded /etc/bashrc
loaded ~/.bash_rc
loaded ~/.bash_profile
graph TB
1(/etc/profile) -- 步骤 1--> 1
2(/root/.bash-profile) -- 步骤 2--> 3
3(/root/.bash-rc) -- 步骤 3--> 4
4(/etc/bashrc) -- 步骤 4 --> 3
3 -- 步骤 5 --> 2
> 上述图中
>
> 因为 `~` 会被本义, 因而用具体的 `/root/` 代替
>
> 因为 `_` 显示不进去, 因而用 `-` 代替
**no-login shell**
- 包含: `su` 不加减号
- 仅 `bashrc` 类的文件会被加载到.
- 何时开始加载: 当运行 `bash` 时
- 加载程序如下:
su root
loading ~/.bash_rc
loading /etc/bashrc
loaded /etc/bashrc
loaded ~/.bashrc
- 这种形式配置加载是不齐全, 和失常登录环境不 i 一样, 因而个别不倡议应用.
### /etc/profile
系统启动和终端启动时的零碎环境初始化
### /etc/bashrc
函数和命令别名
## 字符串解决
变量默认值相干
不扭转原变量值
${变量名 - 默认值} # 变量未定义, 应用默认值
${变量名:- 默认值} # 变量为空时, 应用默认值. 为空包含未定义或空(只蕴含一个空格的不失为空)
${变量名:+ 默认值} # 变量不为空时, 应用默认值
扭转原变量
${变量名 = 默认值} # 变量未定义, 应用默认值, 同时批改原变量
${变量名:= 默认值} # 变量为空时, 应用默认值, 同时批改原变量
间接报错
${变量名:? 提醒文本} # 变量为空时提醒报错
字符串操作
${# 变量名} # 字符串长度
${变量名:pos:length} # 从地位 pos(下标从 0 开始)开始提取字串 length 个字符.
# pos 可省略, 默认为 0
# length 可省略, 默认为到字符串结尾
${变量名 #substring} # 前缀匹配, 删除匹配的字串(非贪心模式)
# substring 要删除的字串, 反对 "通配符"
${变量名 ##substring} # 贪心模式
${变量名 %substring} # 后缀匹配, 删除匹配的字串(非贪心模式)
${变量名 %%substring} # 贪心模式
${变量名 /substring/replace} # 匹配所有, 并替换第一个匹配
${变量名 //substring/replace} # 匹配所有, 并替换所有
${变量名 /#substring/replace} # 前缀匹配, 并替换
${变量名 /%substring/replace} # 后缀匹配, 并替换
> 上述的匹配是 "通配符" 匹配模式
更多可参考: https://linuxeye.com/390.html
## 数组
** 定义数组 **
数组名 =(元素 1 元素 2 元素 3)
留神
元素之间用空格距离开, 若元素自身含有空格, 则须要应用引号蕴含.
() 内的相邻地位不限度是否有空格
显示数组
打印第一个元素
echo $ 数组名
打印数组所有元素
echo ${数组名[@]}
显示数组元素个数
echo ${# 数组名[@]}
显示数组第一个元素
echo $ 数组名
显示数组某个元素
echo ${数组名[n]} # 此处 n 示意元素下标, 从 0 开始
示例
cmdList=(
A1
A2
A3
)
for i in “${cmdList[@]}”; do
if $i; then
echo "success"
else
echo "error with code: $?"
fi
done
# 特殊符号
## 其余字符
- `#` 正文符
- `;` 命令分隔符
- `;;` case 语句应用的分隔符
> 在一行中连贯多条命令, 每个命令在前一个命令执行完后执行.
>
> 前一个工作的执行后果不会影响后续工作.
- `:` 空指令(什么都不做)
可用于循环中作为一个占位符, `:` 永远返回真(即 0).
> 因为在循环中都没有执行语句是会报错的.
- `,` 分隔目录
> `cp 123{txt,log}` 执行成果 `cp 123.txt 123.log`
- `?` 条件测试
- `$` 取值符号
> ```sh
> echo $(命令) # 取运行后果的值
>
> echo ${变量名} # 取变量的值
> echo ${# 变量名} # 取变量长度
>
> echo ${变量名[@]} # 取数组的值
> echo ${# 变量名[@]} # 取数组的长度
>
> echo ${! 变量名} # 取变量名的值所对应的变量值. 即间接取值.
> # x=y; y=z; echo ${!x} # 后果是 z
> ```
- `|` 管道符
- `&` 后盾运行
- shell 下专用
- `.` 等价于 source 命令
- `~` home 目录
- `-` 上一次目录
`cd -`
- `*` 通配符(任意字符)
- `?` 通配符(1 个任意字符)
- ` ` 空格
## 本义
- 一般字符本义赋予不同性能
`\n`, `\t`, `\r` 单个字母的本义
- 特殊字符本义成一般字符用
`\$`, `\"`, `\'` `\\` 单个非字母的本义(即不本义)
## 援用
- `"` 双引号: ** 不齐全援用 **
不齐全援用, 会解释双引号其中的变量.
a=”$SHELL” # 值为 /bin/bash
- `'` 单引号: ** 齐全援用 **
齐全援用(RAW), 不解释其中的变量.
a=’$SHELL’ # 值为 $SHELL
- `` ` 反引号: ** 执行命令 **
等价 `$()`
a=whoami
# 值为 以后用户名
## 括号
独自应用和非独自应用的意义通常是不一样的.
- `()`, [`(())`](# 双圆括号 - let 命令的简化), `$()` 圆括号
# 独自应用, 会产生一个子 shell
() # eg. (a=123) 执行完这个命令时, 因为是在子过程 (子 Shell) 中执行, 因而不会影响以后 shell 环境. (???? 想一下管道)
# 数组初始化
变量名 =(数组元素)
# 算数运算符
(()) # 等价 let 命令的简写, eg. ((i++))
# 执行命令并将后果赋值给变量
变量名 =$(命令) # 等价于 变量名 = 命令
- `[]`, `[[]]` 方括号(test 测试)
# 独自应用, 测试(test)
[] # 等价 test 命令, 应用 -gt, -lt 等. 方括号与内容需放弃距离. 可通过 $? 查看 test 后果
# 测试表达式, [] 的增强, 反对扩大语法: &&, ||, <, > 等
[[]] # 方括号与内容需放弃距离. 可通过 $? 查看 test 后果
- `<`, `>` 尖括号(重定向))
重定向符号
- `{}` 花括号 (范畴, 枚举)
# 输入范畴
echo {0..9} # 0 1 2 3 4 5 6 7 8 9
echo a{1,3,5} # a1 a3 a5
# 文件复制的快捷操作等
cp /etc/passwd{,.bak} # 理论执行的是 cp /etc/passwd /etc/passwd.bak
# 范畴
for i in {1..9}; do echo $i; done;
## 运算符和逻辑符号
- `+`, `-`, `*`, `/`, `%` 算术运算符
- `>`, `<`, `=` 比拟运算符
- `&&`, `||`, `!` 逻辑运算符
## 算术运算
### expr 运算
应用 `expr` 运算
expr < 运算局部 >
示例
expr 4 + 5
a=`expr 4 + 5` # 将后果赋值给变量
留神
只反对整数, 不反对浮点数
数值和运算符之间 "必须" 有空格分隔
### let 命令
let 变量名 = 变量值
示例
let a=10+20 # a 的值是 30
留神
变量值能够是数学表达式, 包含: + - ++ -- += -= 等
变量值不反对浮点数.
变量值 `0` 结尾为八进制.
变量值 `0x` 结尾为十六进制.
> 理论很少用 `let`, 而是应用更简便的 双圆括号.
>
> 数值和运算符之间无所谓有没有空格
### 双圆括号 - let 命令的简化
[点击查看其余括号](# 括号)
双圆括号是 let 命令的简化
语法
(()) # 赋值 / 运算
$(()) # 援用计算结果
示例
((a=10))
((a++))
((a--))
((a+=5))
((a=b=c=1))
echo $((10+20)) # 打印后果
b=$((1+2+3)) # 援用后果
# 测试与判断
## 退出与退出状态
程序
- `exit` 退出程序, 返回状态以 `exit` 的上一条命令执行后果为准
- `exit < 返回值 >` 退出程序, 返回状态以此处填写的 返回值 (只能是数字) 为准.
函数
- `return` 返回状态以 `return` 的上一条命令执行后果为准
- `return < 返回值 >` 退出程序, 返回状态以此处填写的 返回值 (只能是数字) 为准
个别约定, 返回值 `0` 示意失常, 其余都是不失常退出.
应用 `$?` 能够查看返回值, 用于确定 ** 以后 Shell** 的上一个执行语句 (过程, 脚本, 函数) 是不是失常退出.
## 测试命令 test
`[]` 等价于 test 命令, 更举荐 `[]` 写法.
`[[]]` 是 `[ ]` 的扩大写法, 反对 `&&`、`||`、`<`、`>`
> 若要应用 `&&`、`||`、`<`、`>` 这些字符, 则 ** 必须 ** 用 `[[]]`
test 命令用于检测文件或比拟值
- 文件测试
- 数值比拟测试
- 字符串测试
返回值阐明
真(True) 返回 0
假(False) 返回 1
语法
test 表达式
test
[表达式]
[ ]
表达式
逻辑表达式
表达式 1 -a 示意 2 # 逻辑与
表达式 1 -o 表达式 2 # 逻辑或
! 表达式 # 逻辑非
字符串表达式
-z STR # zero, 字符串长度为 0
-n STR # non-zero, 字符串长度不为 0
STR1 = STR2 # 字符串相等测试(大小写敏感)
STR1 != STR2 # 字符串不相等测试(大小写敏感)
数值表达式
-eq # =
-ge # >=
-gt # >
-le # <=
-lt # <
-ne # !=
文件
不同文件比拟
FILE1 -ef FILE2 # 雷同文件(同个设施, 雷同 inode)
FILE1 -nt FILE2 # FILE1 批改工夫比 FILE2 更新(也就是最近批改)
FILE1 -ot FILE2 # 与 -nt 相同
文件
-e FILE # 文件存在
-s FILE # 长度大于 0 的文件
类型
-f FILE # 一般文件(非下述几种类型)
-b FILE # block 块设施
-c FILE # char 字符设施
-d FILE # dir 目录
-h FILE # 软连贯文件, 等同 -L
-L FILE # link 软连贯文件, 等同 -h
-p FILE # 命名管道文件
-S FILE # socket 文件
归属
-O FILE # 文件有无效的属主
-G FILE # 文件有无效的属组
权限
-r FILE # 已设置读权限
-w FILE # 已设置写权限
-x FILE # 已设置执行权限
-u FILE # 已设置 SUID(set-user-ID)
-g FILE # 已设置 SGID(set-group-ID)
-k FILE # 已设置 SBIT(sticky bit set)
其余
-t FD # 文件描述符在终端上关上
### `[[]]` 扩大用法
此处记录的是与 `[]` 不同的
表达式
== # 通配符匹配, 反对: * ?
=~ # 正则匹配
&&
||
<
>
> 不反对 `-a` , `-o`
## 条件 if
残缺语法
if [测试条件]
then 执行相应命令 # 如果条件成立 或 返回值为 0 则进入 then, then 前面无需跟分号
elif [测试条件] # 即 else if
else 执行相应命令
fi # 完结
能够写成 1 行
if [测试条件]; then 执行相应命令; else 执行相应命令; fi
调试时 if true
if :; then … fi
调试时 if false
if [! :]; then … fi
判断执行某个命令后的后果
if 命令; then
echo "success"
else
echo "fail"
fi
> `< 测试条件 >` 如果是个命令或执行的脚本或函数, 那么此时是依据其返回的后果值来判断是否为真.
>
> if 语句反对嵌套应用.
## 分支 case
残缺语法
case “$ 变量 ” in
状况 1)
命令
;;
状况 2)
命令
;;
*) # 此处用了通配符, 匹配其余状况
都不匹配时执行的命令
;;
esac
匹配条件反对如下通配符
- `*`
- `?`
- `|`
- `[ ]` 示意范畴内的任意一个字符, 范畴内能够应用 `-`
- `[^]` 逆向抉择 ?
- `[!]` 逆向抉择 ?
> 上述的 状况 反对通配符, 比方 `*`, `|`, `?`
>
>
>
> 留神以下两种是不同的示意
>
> - `"状况)"` 此处是将 ` 状况 ` 视为一个字符串, 其中的通配符之类的都不会失效
> - ` 状况)` 此处的 ` 状况 ` 中的通配符之类的失效
>
> 因而以下几种匹配是不一样的
>
> ```sh
> start|stop) # 匹配 start 或 stop
>
> "start"|"stop") # 同上, 匹配 start 或 stop
>
> "start|stop") # 残缺匹配 "start|stop" 整个字符串
>
> "cmd*") # 残缺匹配 "cmd*" 整个字符串
>
> "cmd?") # 残缺匹配 "cmd?" 整个字符串
>
> cmd*) # 通配符匹配 cmd*
>
> cmd?) # 通配符匹配 cmd?
> ```
## 循环
### for 遍历
for 参数名 in 列表
do
:
done
** 列表起源 **
1. 列表中蕴含多个变量(空格分隔)
for i in 1 2 3
for i in {1..3} # 应用花括号产生列表
2. 应用 ` `` ` 或 `$()` 形式执行命令, 默认逐行解决(若呈现空格会被视为多行)
for file in `ls`
3. 枚举门路(可应用通配符)
# 枚举出的是残缺门路, 可配合应用 basename 命令获取简短的文件名
for file in /etc/profile*
do
echo $file
done
# 输入如下
/etc/profile
/etc/profile.d
### for 循环
C 语言格调的 for 命令
语法
for ((变量初始化; 循环判断条件; 变量变动))
do
命令
done
示例
for ((i=0;i<=10;i++)); do echo $i; done
> 不太罕用
### while 循环
语法
# 满足条件就执行
while test 测试条件为真
do
命令
done
示例
i=0
while [[i<=10]]
do
((i+=3))
done
> 罕用于构建交互式菜单
可配合 `shift` 命令偏移参数解决
地位参数位移, 如果偏移数为 n, 则新的 $1 值为原来的 ${n+1}, 以此类推
shift < 偏移数 =1>
示例
while [$# -gt 0] do
shift 1
done
### until 循环
语法
# 不满足条件就执行
until test 测试条件为假
do
命令
done
### break 和 continue
while :
do
break;
continue;
done
# 函数
## 自定义函数
定义
函数名称 () {
local 函数局部变量名; # 变量作用域仅在函数外部
echo $1; #
}
留神
语法要求: 第一个花括号前面须要有空(空格, 换行等)
应用
函数名
可将自定义的函数对立放在一个脚本文件中, 赋予执行权限后通过 `source` 或 `.` 执行以在以后 Shell 环境调用.
示例
hello() { echo hello $USER;}
hello # 输入 hello root
程序执行多个函数的示例
A1 () { :}
A2 () { :}
A3 () { :}
cmdList=(
A1
A2
A3
)
for i in “${cmdList[@]}”; do
if $i; then
echo "success"
else
echo "error with code: $?"
fi
done
## 获取函数名
在函数外部有一些预约义变量
$FUNCNAME 数组, 蕴含调用栈名
示例
!/bin/bash
abc() {
echo "in abc: ${FUNCNAME[@]}"
ef
}
ef() {
echo "in ef: ${FUNCNAME[@]}"
}
abc
输入内容
in abc: abc main
in ef: ef abc main
## 删除函数
unset -f 函数名
## 零碎脚本
零碎自建的函数库: `/etc/init.d/functions`
# 脚本管制
## 脚本优先级管制
能够应用 nice 和 renice 调整脚本优先级
CPU 的计算和创立子过程都会造成零碎 开销.
创立死循环大量耗费 cpu 导致死机的状况
1. while 和 for 创立死循环会导致 cpu 占用过高,
2. fork 炸弹: 程序大量创立子过程, CPU 不响应任何信息.
# 示例 – 定义一个 func 的函数
func () { func | func&}
# 调用之后零碎会疯狂创立子过程, 此时 ctrl+c 曾经不失效了
func
# 另一个常见的 fork 炸弹
.(){ .|.&}; .
`ulimit -a` 的输入
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 5642
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 5642 # 同时可创立的子过程数量, root 有效
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
> 大部分限度对 root 是不失效的.
## 捕捉信号
`kill` 默认发送 15 号 (SIGTERM) 信号给应用程序
`ctrl+c` 发送 2 号 (SIGINT) 信号给应用程序
9 号 (SIGKILL) 信号不可捕捉, 不可阻塞, 强行杀过程.
设置捕捉信号
trap [参数] 信号
< 参数 > 示意捕捉到信号后的解决, 分为以下几种状况
未设置 < 参数 > 按默认解决状况解决
值为 - 按默认解决状况解决, 同上
值为 空 捕捉信号, 间接疏忽
值为其余 捕捉信号, 并执行指定命令
示例
# 捕捉 SIGINT(2) 信号, 并打印 "got signal 2"
trap "echo got signal 2" 2
> 可在重要不可中断的脚本中设置捕捉信号免得被无心中完结掉, 比方备份脚本.
control group? 管制内存?
# 打算工作
## at 命令
设置一次性打算工作
输出要执行的工作后须要追加 ctrl+d 以示意输出结束.
at [选项] 工夫
工夫格局
HH:MM # 时: 分 指定下一个该工夫点执行(明天或隔天)
HH:MM today # 明天指定工夫运行
HH:MM tomorrow # 今天指定工夫运行
HH:MM MMDDYY # 时: 分 月日天, 年能够是 2 位的缩写, 或 4 位的全写
HH:MM MM/DD/YY
HH:MM MM.DD.YY
HH:MM YYYY-MM-DD
now + 计数 # 以后工夫点的偏移, 计数单位: minutes, hours, days, weeks. Eg. 4pm + 3 days
选项
-c < 序号 > # 查看具体的工作执行内容
-f <file> # 从指定文件读取命令, 而不是从规范输出
留神
1. 非外部命令应应用残缺门路, 如果是 shell 脚本应应用 source 来引入环境变量
2. 打算工作的执行是没有终端的, 因而是没有规范输入的, 需自行重定向输入
Tip
鉴于 at 在设置命令时的不不便, 能够思考配合 cat 一起设置, eg.
cat <<'EOF' | at 工夫
具体要执行的命令
EOF
留神
- `at` 一次性工作是依赖 atd 服务来执行的
- `at` 目录会以设置工作时所在的目录作为工作目录, 因而若该目录有效(被删除, 权限限度) 则会导致工作执行失败.
- root 用户能够在任何状况下应用 at 命令,而其余用户应用 at 命令的权限定义在 /etc/at.allow(被容许应用打算工作的用户)和 /etc/at.deny(被回绝应用打算工作的用户)文件中,默认没有文件须要本人创立容许用户和回绝用户文件;- 如果 /etc/at.allow 文件存在,只有在该文件中的用户名对应的用户能力应用 at;- 如果 /etc/at.allow 文件不存在,/etc/at.deny 存在,所有不在 /etc/at.deny 文件中的用户能够应用 at;- at.allow 比 at.deny 优先级高,执行用户是否能够执行 at 命令,先看 at.allow 文件中有没有才看 at.deny 文件;- 如果 /etc/at.allow 和 /etc/at.deny 文件都不存在,则只有 root 用户能应用 at;**atq 命令 **
查问期待执行的一次性打算工作队列
最右边的数字即打算工作的工作 id
atq
**atrm 命令 **
移除未执行的打算工作
atrm 工作序号
## 周期性打算工作 cron
crontab [选项]
选项
-e # 编辑, 进入 vim 编辑器
-l # 查看已配置项
配置格局
分 时 日 月 周 命令
* # 任意
1,2,3 # 逗号分隔
1-3 # 等价示意 1,2,3
留神
命令应应用残缺门路
配置文件(每个用户有各自的一份配置)
`/var/spool/cron/ 用户名 `
crond 相干日志(不包含工作的输入)
`/var/log/cron`
## 打算工作和锁
### anacron 周期命令调度程序
anacron 是一个用于周期性执行命令的工具(实用于非 24 小时开机, 同时须要确保 每日 / 每周 / 每月 运行指定工作).
它的工夫粒度是 "天", 比方配置了 logrotate 每天运行一次, 那么它默认会在 3~22 点之间每小时尝试运行该工作(因而主机不肯定须要放弃 24 小时开机)
** 适宜 **: 须要定期执行至多 1 次的脚本 (对具体执行机会没有严格要求的) 能够应用 anacron 来配置定时工作.
> 比方要求每 3 天至多执行 1 次, 但对于在哪一天的哪一个小时执行没有严格要求.
**Crond 服务调用 Anacron 的过程 **
1. crond 服务每分钟会执行 `/etc/cron.d/` 目录下配置的定时工作(crontab 格局):
- `/etc/cron.d/0hourly` 配置每小时的第 1 分钟执行 `/etc/cron.hourly/` 目录下的所有 **<u> 脚本 </u>**
- `/etc/cron.d/sysstat` 配置每 10 分钟零碎性能统计收集, 每天靠近凌晨时生成一份每日报告
2. `/etc/cron.hourly/0anacron` 脚本
> 脚本的次要逻辑: 若今日未执行过 anacrontab, 则会执行 `/usr/sbin/anacron -s` 命令
>
> 意思是程序 (非并行) 执行延时打算工作.
**Anacron 的执行过程 **
1. 读取配置文件 `/etc/anacrontab`
# the maximal random delay added to the base delay of the jobs
RANDOM_DELAY=45
# the jobs will be started during the following hours only
START_HOURS_RANGE=3-22
#period in days delay in minutes job-identifier command
1 5 cron.daily nice run-parts /etc/cron.daily
7 25 cron.weekly nice run-parts /etc/cron.weekly
@monthly 45 cron.monthly nice run-parts /etc/cron.monthly
2. 顺次执行下面配置的工作
Anacron 会在每天的 3 点~22 点工夫范畴内, 每次随机提早 0~45 分钟开始执行下面配置的 每日 / 每周 / 每月 工作.
- `/etc/cron.daily/logrotate` 日志轮转工具调用
- ...
### flock 锁文件
flock [选项]
示例
flocak -xn "/tmp/f.lock" -c "需执行的命令或脚本"
选项
锁类型
-x # 排他锁
-n, --nb, --nonblock # 加锁失败时间接退出 (默认是期待)
-c, --command <command> # 需执行的命令
留神
锁文件被删除后会生效.
# 其余待整顿
##
# run-parts 命令
运行指定目录下的所有可执行文件(具备执行权限)
- 可通过在目录下配置 jobs.deny 和 jobs.allow 来配置黑 / 白名单
- 罕用于 crond 定时工作执行某个目录下的所有脚本
run-parts < 目录 >
## mktemp 命令
创立一个临时文件或目录
mktemp [选项] [模板]
模板
需蕴含至多间断 3 个 "X", 若未指定, 则会在 /tmp 目录下创立 tmp.XXXXXXXXXX
选项
-d, --directory # 创立一个目录(而不是默认的文件)
-t # 在 /tmp 目录下创立临时文件
示例
mktemp # /tmp/tmp.CaE8KvS8HI
mktemp log.XXXXXXX # log.KyvESFk 在当前目录下
mktemp -t log.XXXX # /tmp/log.RNwC
## yes 命令
在命令行中输入指定的字符串,直到 yes 过程被杀死
yes [选项] <string=y>
示例
常用语须要简略交互: 输出 y/yes 以持续操作的状况
yes|yum install ... # 这里只是示例, 理论 yum 个别是配合 -y