[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 的疏导步骤

  1. /etc/rc.d/rc.sysinit 零碎初始化工作
  2. 期待用户终端接入

在CentOS 7中, systemd 的步骤

  1. /etc/systemd/system 读取启动级别
  2. /usr/lib/systemd/system 读取各个service
# 导出主疏导记录dd if=/dev/sda of=mbr.bin bs=446 count=1hexdump -C mbr.bin                            # 查看主疏导记录# 导出主疏导记录和磁盘所有分区表dd if=/dev/sda of=mbr2.bin bs=512 count=1hexdump -C mbr2.bin                            # 最初面以 55aa 结尾示意可疏导

如何编写shell脚本

规范的 Shell 脚本要蕴含的元素

  • Sha-Bang

    首行的 #! 结尾的局部

    文本文件首行增加 #!/bin/bash 能够在以 ./脚本.sh 这种形式执行脚本时申明以后是 bash 脚本, 零碎会自行抉择对应的 shell 来执行, 若是以 bash 脚本.sh 则会被视为正文.

  • # 结尾的视为正文
  • 脚本执行权限, 若是二进制可执行文件只须要 x, 若是文本文件则须要 rx
  • 通常约定bash脚本的扩大名为 .sh
  • 在一行中可应用 ; 分隔多条命令, 会顺次按程序一一执行命令, 只有在前一个命令执行完才会执行后一个命令.

确保脚本执行谬误时马上退出

可在脚本结尾设置: set -e , 从而告知 bash, 若有任何语句执行失败, 就间接退出脚本, 避免谬误像滚雪球般变大.

此时 $? 无奈应用

若在 set -e 模式下为了确保某些语句失败不退出脚本, 可采纳如下形式

# 办法1command || { echo "command failed"; exit 1; }# 办法2if ! command; then    echo "command failed"    exit 1fi

对于 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 | catecho 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 echoecho -e "a\nb\nc" | xargs -n 1 echoecho "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 pipefailset -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--> 12(/root/.bash-profile) --步骤 2--> 33(/root/.bash-rc) --步骤 3--> 44(/etc/bashrc) --步骤4--> 33 --步骤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 + 5a=`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=0while [[ i<=10 ]]do    ((i+=3))done
> 罕用于构建交互式菜单 可配合 `shift` 命令偏移参数解决

地位参数位移, 如果偏移数为n, 则新的 $1 值为原来的 ${n+1}, 以此类推

shift <偏移数=1>

示例

while [ $# -gt 0 ] do    shift 1done
### 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/YYHH:MM MM.DD.YYHH:MM YYYY-MM-DDnow + 计数      # 以后工夫点的偏移, 计数单位: 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.CaE8KvS8HImktemp log.XXXXXXX        # log.KyvESFk            在当前目录下mktemp -t log.XXXX        # /tmp/log.RNwC
## yes 命令

在命令行中输入指定的字符串,直到yes过程被杀死

yes [选项] <string=y>

示例

常用语须要简略交互: 输出 y/yes 以持续操作的状况yes|yum install ...        # 这里只是示例, 理论 yum 个别是配合 -y