总字符数: 38.29K
代码: 22.63K, 文本: 15.66K
预计阅读时间: 1.28 小时
Shell基础
Shell的简介
Shell的本意是“壳”的意思,其实已经很形象地说明了shell在Linux系统中的作用。Shell就是围绕在Linux内核之外的一个“壳”程序,用户在操作系统上完成的所有任务都是通过shell与Linux系统内核的交互来实现的 。
所以也可以认为Shell是用户和Linux操作系统之间的接口。Linux中有多种shell,其中缺省使用的是bash 。
Shell最重要的功能是命令解释,从这种意义上说,Shell是一个命令解释器。Linux系统中的所有可执行文件都可以作为Shell命令来执行。Linux系统上可执行文件的分类见下表。

当用户提交了一个命令后,Shell首先判断它是否为内置命令.
如果是就通过Shell内部的解释器将其解释为系统功能调用并转交给内核执行;
若是外部命令或实用程序就试图在硬盘中查找该命令并将其调入内存,再将其解释为系统功能调用并转交给内核执行。
用户登录系统后,如果登录字符界面,将出现shell命令提示符。
- “#”表示登录的用户是系统超级用户
- “$”表示登录到系统的是普通用户
Shell还是强大的解释行程序设计语言,它定义了各种选项和变量,几乎支持高级程序语言的所有结构,如变量、函数、表达式和循环等。
利用shell可以编写shell脚本程序,类似于Windows/DOS下的批处理文件,但是shell功能更加完善,更加强大。
归纳 Shell是一个命令行解释器,它为用户提供了一个向Linux内核发送请求以便运行程序的界面系统级程序,用户可以用Shell来启动、挂起、停止甚至是编写一些程序。
Shell还是一个功能相当强大的编程语言,易编写,易调试,灵活性较强。Shell是解释执行的脚本语言,在Shell中可以直接调用Linux系统命令。
Bash是作为用户的基本Shell(默认)。
命令学习#### echo输出命令echo [选项] [输出内容]
选项:-e 支持反斜线控制的字符转换
echo中间有空格需要加双引号,没有的话可以直接写,!在Linux中有特殊作用,如果非要加使用单引号
1 | echo 123456 |
控制字符| 控制字符 | 作用 |
| — | — |
| \ | 输出\本身 |
| \a | 输出警告音 |
| \b | 退格键,也就是向左删除键 |
| \c | 取消输出行末的换行符。和“-n”选项一致 |
| \e | ESCAPE键 |
| \f | 换页符 |
| \n | 换行符 |
| \r | 回车键 |
| \t | 制表符,也就是tab键 |
| \v | 垂直制表符 |
| \0nnn | 按照八进制ASCII码表输出字符。其中0为数字0,nnn为三位八进制数 |
| \xhh | 按照十六进制ASCII码表输出字符。其中hh是两位十六进制数 |
1 | echo -e "ab\bc" |
date时间命令这个命令在shell脚本中使用很频繁,最常见的几个用法如下:
date +%Y
:表示以四位数字格式打印年份date +%y
:表示以两位数字格式打印年份date +%m
:表示月份date +%d
:表示日期date +%H
:表示小时date +%M
:表示分钟date +%S
:表示秒date +%w
:表示星期,0~6,0表示星期天
参数说明-d datestr
: 显示 datestr 中所设定的时间 (非系统时间)–help
: 显示辅助讯息-s datestr
: 将系统时间设为 datestr 中所设定的时间-u
: 显示目前的格林威治时间–version
: 显示版本编号
1 | [root@localhost ~]# date +"%Y-%m-%d %H:%M:%S" |
脚本执行方式Shell脚本通常都以.sh作为后缀名,不是说不加.sh的脚本不能运行,只是大家的习惯,这样也方便辨识。
创建脚本```bash
不是注释,是一个标志,标称写的内容为Bash,Shell脚本
#!/bin/bash
#The first program #一定要写完整的注释
#Author:kali #一定要有良好的注释
date
echo -e “Hello World!”
echo -e ‘\e[1;31mHello World!\e[0m’
1 | 上面,第一行以 `#!/bin/bash` 开头,表示该文件使用的是bash语法,不设置该行也可以执行,但不符合规范。 |
Shell基本功能### Shell 元字符在Shell中有一些具有特殊的意义字符,称为 Shell元字符(shell metacharacters)。若不以特殊方式指明,Shell并不会把它们当做普通文字符使用。
下表简单介绍了常用的Shell元字符的意义:
历史命令和命令补全```bash
[root@localhost ~]#history [选项] [历史命令保存文件]
选项:
-c: 清空历史命令
-w: 把缓存中的历史命令写入历史命令保存文件
~/.bash_history #历史命令保存器
历史命令默认会保存1000条可以在环境变量配置文件/etc/profile中进行修改
1 | #### 历史命令的调用- 使用上、下箭头调用以前的历史命令 |
让别名永久有效```bash
[root@localhost ~]#vi /root/.bashrc
1 | #### 删除别名```bash |
命令执行时顺序1. 第一顺位执行用绝对路径或相对路径执行的命令
- 第二顺位执行别名
- 第三顺位执行Bash的内部命令
- 第四顺位执行按照$PATH环境变量定义的目录查找顺序找到的第一个命令
多命令顺序执行| 多命令执行符 | 格式 | 作用 |
| — | — | — |
| ; | 命令1;命令2 | 多个命令顺序执行,命令之间没有任何逻辑联系 |
| && | 命令1&&命令2 | 逻辑与 当命令1正确执行,则命令2才会执行 当命令1执行不正确,则命令2不会执行 |
| | | |
1 | [root@localhost ~]#ls /root;ls /root/kali |
1 | #dd命令是Linux磁盘复制或数据复制,能复制特殊命令,特殊文件,也能复制分区,甚至整个硬盘,可以当做磁盘对拷对待 |
Bash常用快捷键

Shell的重定向Linux下系统打开3个文件,即标准输入、标准输出和标准错误输出。
用户的shell将键盘设为默认的标准输入,默认的标准输出和标准错误输出为屏幕。
也就是,用户从键盘输入命令,然后将结果和错误消息输出到屏幕。
所谓的重定向,就是不使用系统默认的标准输入/输出,而是重新指定,因此重定向分为输入重定向、输出重定向和错误输出重定向要实现重定向就需要了解重定向操作符,shell就是根据重定向操作符来决定重定向操作的。
输入重定向```bash
[root@localhost ~]#wc [选项] [文件名]
选项:
-c 统计字节数
-w 统计单词数
-l 统计行数
1 | 输入重定向用于改变命令的输入源,利用输入重定向,就可以将一个文件的内容作为命令的输入,而不从键盘输入。(CTRL+D) |
输出重定向输出重定向不是将命令的输出结果在屏幕上输出,而是输出到一个指定文件中。
在Linux下输出重定向用得很多。例如,某个命令的输出很长,一个屏幕无法显示完毕,这时可以将命令的输出指定到一个文件,然后用more命令查看这个文件,从而得到命令输出的完整信息。
| 类型 | 符号 | 作用 |
|---|---|---|
| 标准输出重定向 | 命令 >文件 命令 >>文件 | 以覆盖的方式,把命令的正确输出输出到指定的文件或设备当中 以追加的方式,把命令的正确输出输出到指定的文件或设备当中 |
| 标准错误输出重定向 | 错误命令 2>文件 错误命令 2>>文件 | 以覆盖的方式,把命令的错误输出输出到指定的文件或设备当中 以追加的方式,把命令的错误输出输出到指定的文件或设备当中 |
用于输出重定向的操作符有>和>>。例如: |
1 | ps -ef >ps.txt |
错误重定向实际工作中,正确输出和错误输出同时保存
错误重定向和标准输出重定向一样,可以使用操作符2>和2>>实现对错误输出的重定向
| 命令 > 文件 2>&1 | 以覆盖的方式,把正确输出和错误输出都保存到同一个文件当中 |
|---|---|
| 命令 >> 文件 2>&1 | 以追加的方式,把正确输出和错误输出都保存到同一个文件当中 |
| 命令 &>文件 | 以覆盖的方式,把正确输出和错误输出都保存到同一个文件当中 |
| 命令 &>>文件 | 以追加的方式,把正确输出和错误输出都保存到同一个文件当中 |
| 命令 >>文件1 2>>文件2 | 把正确的输出追加到文件1中,把错误的输出追加到文件2中 |
1 | [root@localhost ~]# tar zxvf text.tar.gz 2>error.txt |
Shell的管道管道可以把很多命令连接起来,可以把第1个命令的输出当作第2个命令的输入,第2个命令的输出当作第3个命令的输入,依次类推。因此,管道的作用就是把一个命令的输出当作下一个命令的输入,而不经过任何中间文件。
通过管道符“|”可以建立管道连接
1 | [root@localhost ~]# ls -al /etc/* |more |
Shell的通配符通配符主要是为了方便用户对文件或目录的描述,例如,当用户仅仅需要以.sh结尾的文件时,使用通配符就能很方便地实现。各个版本的shell都有通配符,这些通配符是一些特殊字符,用户可以在命令行的参数中使用这些字符,进行文件或者路径名的匹配。Shell将把与命令行中指定的匹配规则符合的所有文件名或者路径作为命令的参数,然后执行这个命令。
Bash中常用的通配符有* ? []*匹配任意一个或多个字符
1 | [root@localhost ~]# ls *.txt |
?匹配任意单一字符
1 | [root@localhost ~]# ls ab?.txt |
[]匹配任何包含在方括号内的单子符
[]
匹配中括号中任意一个字符。例如[abc]代表一定匹配一个字符,或者是a,或者是b,或者是c[-]
匹配中括号中任意一个字符,-代表一个范围。例如:[a-z]
代表匹配一个小写字母[^]
逻辑非,表示匹配不是中括号内的一个字符。例如:[^0-9]
代表匹配一个不是数字的字符
1 | [root@localhost ~]# ls /dev/sda[12345] |
通配符的组合使用
1 | [root@localhost ~]# ls [0-9]?.conf |
Shell中其他特殊符号在bash中有很多特殊字符,这些字符本身就具有特殊含义。如果在shell的参数中使用它们,就会出现问题。Linux中使用了“引用”技术来忽略这些字符的特殊含义,引用技术就是通知shell将这些特殊字符当作普通字符处理。Shell中用于引用的字符有转义字符\、单引号''、双引号""。
转义字符如果将\放到特殊字符前面,shell就会忽略这些特殊字符的原有含义,把它们当作普通字符对待.
1 | [root@localhost ~]# ls |
单引号如果将字符串放到一对单引号之间,那么字符串中所有字符的特殊含义将被忽略.
1 | [root@localhost ~]# mv C\:\\backup backup |
双引号双引号的引用与单引号基本相同,包含在双引号内的大部分特殊字符可以当作普通字符处理,但是仍有一些特殊字符即使用双引号括起来,也仍然保留自己的特殊含义,比如$、\,’
1 | [root@localhost ~]# str="The \$SHELL Current shell is $SHELL" |
1 | [root@localhost ~]# str="This hostname is 'hostname'" |
1 | [root@localhost ~]# name=kali # 声明变量 |
Bash变量### 什么是变量变量是计算机内存的单元,其中存放的值可以改变。当Shell脚本需要保存一些信息时,如一个文件名或是一个数字,就把它存放在一个变量中。每个变量有一个名字,所以很容易应用它。使用变量可以保存有用信息,使系统获知用户相关设置,变量也可以用于保存暂时信息。
变量设置规则- 变量名称可以由字母、数字和下划线组成,但是不能以数字开头。如果变量名是
2name
则是错误的。
- 在Bash中,变量的默认类型都是字符型,如果要进行数值运算,则必须指定变量类型为数值型。
- 变量用等号连接值,等号左右两侧不能有空格。
- 变量的值如果有空格,需要使用单引号或双引号包括。
- 在变量的值中,可以使用
\
转义符。 - 如果需要增加变量的值,那么可以进行变量值的叠加。不过变量需要用双引号包含
$变量名
或用${变量名}
包含。 - 如果是把命令的结果作为变量值赋予变量,则需要使用反引号或
$()
包含命令。 - 环境变量名建议大写,便于区分。
变量分类- 用户自定义变量(最常用的)
- 环境变量:这种变量中主要保存的是和系统操作环境相关的数据
- 位置参数变量:这种变量主要是用来向脚本当中传递参数或数据的,变量名不能自定义,变量作用是固定的
- 预定义变量:是Bash中已经定义好的变量,变量名不能自定义,变量作用也是固定的。
用户自定义变量(本地变量)```bash
[root@localhost ~]# name=”kali”
变量定义,有空格用双引号或单引号括起来
1 | #### 变量叠加```bash |
变量调用在脚本中应用变量时需要加上符号$。
1 | # 调用变量 |
变量查看```bash
查看系统的所有变量
[root@localhost ~]# set
[root@localhost ~]# set | grep “name”
1 | #### 变量删除```bash |
1 | # 写入下面的内容 |
环境变量#### 环境变量是什么用户自定义变量只在当前的Shell中生效,而环境变量会在当前Shell和这个Shell的所有子Shell当中生效。如果把环境变量写入相应的配置文件,那么这个环境变量就会在所有Shell中生效。
设置环境变量```bash
export 变量名=变量值 # 申明全局变量
env # 专门查询环境变量 重点看PATH
unset 变量名 # 删除变量
1 | ```bash |
系统常见环境变量```bash
PATH:系统查找命令的路径
环境变量
[root@localhost ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@localhost sh]# cp ./hello.sh /root/bin
[root@localhost ~]# hello.sh
Hello World!
PATH变量叠加
PAHT=”$PATH”:/root/sh
[root@localhost ~]# PATH=”$PATH”:/root/sh
[root@localhost ~]# hello.sh
Hello World!
[root@localhost ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/sh
1 | #### PS1:定义系统提示符的变量| 转义序列 | 描述 | |
环境变量配置文件```bash source命令
在Linux中,source 命令或者点命令(.)被用来读取并执行指定文件中的命令。
通常这类文件包含了环境变量的赋值。
使用 source 或者点命令执行文件后,文件中定义的环境变量将会在当前会话中即时生效,而无需重新启动或退出当前会话。
这通常用于修改环境变量配置文件,如 /.bashrc、/.profile 或者是其他自定义的脚本文件。
[root@localhost ~]# source /path/to/configuration_file
或者
[root@localhost ~]# . /path/to/configuration_file
1 | ##### 环境变量配置文件简介环境变量配置文件中主要是定义对系统的操作环境生效的系统默认环境变量,比如PATH(环境变量)/HISTSIZE(历史命令)/PSI(提示符)/HOSTNAME(系统名)等默认环境变量 |
在etc中对所有用户都生效,~只对家目录生效
环境变量配置文件作用description:配置文件的优先级
1 | USER变量: |
其他配置文件和登录信息###### 注销时生效的环境变量配置文件```bash
~/.bash_logout #可以把命令写到这个文件中,系统在注销的时候就会执行这些命令
1 | ###### 其他配置文件```bash |
Shell登录信息```bash
本地终端欢迎信息:/etc/issue
转义符 作用
\d 显示当前系统日期
\s 显示操作系统名称
\l 显示登录的终端号,这个比较常用
\m 显示硬件体系结构,如i386,i686等
\n 显示主机名
\o 显示域名
\r 显示内核版本
\t 显示当前系统时间
\u 显示当前登录用户的序列号
远程终端欢迎信息:/etc/issue.net(不认上面的转义符)
转义符在/etc/issue.net文件中不能使用
是否显示此欢迎信息,由ssh的配置文件/etc/ssh/sshd_config决定,加入“Banner /etc/issue.net”行才能显示(记得重启ssh服务)
重启服务:service sshd restart #重启sshd服务
登陆后欢迎信息:/etc/motd
#不管是本地登陆,还是远程登陆,都可以显示此欢迎信息
1 | ### 位置参数变量| 位置参数变量 | 作用 | |
关于环境变量的脚本```bash
示例1(求和):sum.sh
#!/bin/bash
author:kali
num1=$1
num2=$2
sum=$(($num1+$num2))
变量sum的和是num1加num2
echo $sum
打印变量sum的值
另一种写法
#!/bin/bash
sum=$(($1+$2))
echo “sum is :$sum”
[root@192 sh]# ./sum.sh 33 45
78
示例2:
#!/bin/bash
echo “A total of $# parameters” #使用$#代表所有参数的个数
echo “The parameter is: $*” #使用$*代表所有的参数
echo “The parameter is: $@” #使用$@也代表所有参数
实际我按下面的写
[root@192 sh]# vi parameters1.sh
#!/bin/bash
echo $#
echo $*
echo $@
[root@192 sh]# parameters1.sh
-bash: /root/sh/parameters1.sh: 权限不够
[root@192 sh]# chmod 755 parameters1.sh
[root@192 sh]# parameters1.sh
0
[root@192 sh]# parameters1.sh 11 22 33 44 55 66 #查看参数有几个
6
11 22 33 44 55 66
11 22 33 44 55 66
示例3:$*与$@的区别
[root@192 sh]# vi parameters2.sh
[root@192 sh]# chmod 755 parameters2.sh
[root@192 sh]# parameters2.sh 1 2 3 4
The parameters is : 1 2 3 4
The parameter1 is:1
The parameter2 is:2
The parameter3 is:3
The parameter4 is:4
[root@192 sh]# cat parameters2.sh
#!/bin/bash
for i in “$*”
#$*中的所有参数看成是一个整体,所以这个for循环只会循环一次
do
echo “The parameters is : $i”
done
x=1
for y in “$@”
#$@中的每个参数都看成是独立的,所以”$@”中有几个参数,就会循环几次
do
echo “The parameter$x is:$y”
x=$(($x+1))
done
1 | ### 预定义变量| 预定义变量 | 作用 | |
接收键盘输入```bash
[root@localhost ~]# read [选项] [变量名]
选项:
-p “提示信息”:在等待read输入时,输出提示信息,read命令会一直等待用户输入,使用此选项可以指定等待时间
-n 秒数: read命令只接受指定的字符数,就会执行
-s: 隐藏输入的数据,适用于机密信息的输入
#!/bin/bash
Author:布衣king
read -t 30 -p “Please input your name:” name
echo “Name is $name”
read -s -t 30 -p “Please enter your age:” age
#年龄是隐私,所以我们用“-s”选项隐藏输入
echo -e “\n”
echo “Age is $age”
read -n 1 -t 30 -p “Please select your gender[M/F]:” sex
#使用“-n 1”选项只接收一个输入字符就会执行(都不用输入回车)
echo -e “\n”
echo “Sex is $sex”
#另一种
#!/bin/bash
Author:布衣king
read -t 30 -p “Please input your name:” name
read -s -t 30 -p “Please enter your age:” age
#年龄是隐私,所以我们用“-s”选项隐藏输入
read -n 1 -t 30 -p “Please select your gender[M/F]:” sex
#使用“-n 1”选项只接收一个输入字符就会执行(都不用输入回车)
echo “Name is $name”
echo -e “\n”
echo “Age is $age”
echo -e “\n”
echo “Sex is $sex”
1 | ## Bash的数值运算和运算符### 数值运算```bash |
declare声明变量类型```bash
[root@localhost ~]# declare [+/-][选项] 变量名
选项:
-: 给变量设置类型属性
+: 取消变量的类型属性
-i: 将变量声明为整数型(integer)
-x: 将变量声明为环境变量
-p: 显示指定变量的被声明的类型
[root@192 sh]# declare -p aa
declare – aa=”11”
[root@192 sh]# export aa
[root@192 sh]# declare -p aa
declare -x aa=”11”< /FONT>
1 | #### 数值运算——方法1```bash |
方法2:expr或let数值运算工具```bash
expr英文:简单计算器
[root@192 sh]$ aa=11
[root@192 sh]$ bb=22
#给变量aa和bb赋值
[root@192 sh]$ dd= $(expr $aa+ $bb) #dd的值是aa和bb的和。注意“+”号左右两侧必须有空格,错误结果
[root@192 sh]$ dd=$(expr $aa + $bb) #加空格后的结果 33
[root@192 sh]$ echo $dd
33
[root@192 sh]$ dd=$(expr $aa+$bb) #没有加空格的结果 11+22
[root@192 sh]$ echo $dd
11+22
1 | #### 方法3:`$((运算式))`或`$[运算式]````bash |
运算符(数值越大优先级越高)| 优先级 | 运算符 | 说明 |
| — | — | — |
| 13 | -,+ | 单目负、单目正 |
| 12 | !,~ | 逻辑非、按位取反或补码 |
| 11 | ,/,% | 乘、除、取模 |
| 10 | +,- | 加,减 |
| 9 | <<,>> | 按位左移,按位右移 |
| 8 | <=,>=,<,> | 小于或等于,大于或等于,小于,大于 |
| 7 | ==,!= | 等于、不等于 |
| 6 | & | 按位与 |
| 5 | ^ | 按位异或 |
| 4 | | | 按位或 |
| 3 | && | 逻辑与 |
| 2 | || | 逻辑或 |
| 1 | =,+=,-=,=,/=,%=,&=,^=,|=,<<=,>>= | 赋值、运算且赋值 |
1 | [root@localhost ~]$ aa=$(((11+3)*3/2)) |
Shell编程> Shell脚本是在Linux shell中执行的命令集合,用于自动化执行复杂或重复的任务。将命令写入脚本文件可简化操作并便于维护;建议将自定义脚本存放在/user/local/sbin/目录下以便管理和定时执行任务。
正则表达式#### 正则表达式与通配符正则表达式用来在文件中匹配符合条件的字符串,正则是包含匹配。grep、awk、sed等命令支持正则表达式。
通配符用来匹配符合条件的文件名,通配符是完全匹配。ls、find、cp这些命令不支持正则表达式,所以只能使用shell自己的通配符来进行匹配(* ? [])。
基础正则表达式| 元字符 | 作用 |
| — | — |
| * | 前一个字符匹配0次或任意多次 |
| . | 匹配除了换行符外任意一个字符 |
| ^ | 匹配行首。例如:^hello会匹配以hello开头的行 |
| $ | 匹配行尾。例如:hello$会匹配以hello结尾的行 |
| [ ] | 匹配中括号中指定的任意一个字符,只匹配一个字符。例如:[aoeiu]匹配任意一个元音字母,[0-9]匹配任意一位数字, [a-z][0-9] 匹配小写字和一位数字构成的两位字符 |
| [^] | 匹配除中括号的字符以外的任意一个字符。例如: [^0-] |
| \ | 转义符。用于取消将特殊符号的含义取消 |
| {n} | 表示其前面的字符恰好出现几次。例如:[0-9]{4}匹配4位数字, [1][3-8][0-9]{9} 匹配手机号码 |
| {n,} | 表示其前面的字符出现不小于n次。例如: [0-9]{2,} 表示两位以上的数字 |
| {n,m} | 表示其前面的字符至少出现n次,最多出现m次。例如: [a-z]{6,8} 匹配6到8位的小写字母 |
1 | Mr. Li Ming said: |
"*"前一个字符匹配0次或任意多次
1 | # 匹配所有内容,包括空白行 |
.匹配除了换行符外任意一个字符
1 | # "s..d"会匹配在s和d这两个字母之间一定 有两个字符的单词 |
匹配行首/行尾^匹配行首,$匹配行尾
1 | # 匹配以大写M开头的行 |
[]``[ ]匹配中括号中指定的任意一个字符,只匹配一个字符
1 | # 匹配s和i字母中,要不是a,要不是o |
[^]``[^]匹配除中括号的字符以外的任意一个字符
1 | # 匹配不用小写字母开头的行 |
\转义符```bash
匹配使用“.”结尾的行
[root@localhost kali]# grep “.$” test_rule.txt
1 | #### `{n}``{n}`表示其前面的字符恰好出现几次 |
{n,}``{n,}表示其前面的字符出现不小于n次
1 | # 匹配最少用连续3个数字开头的行 |
{n,m}``{n,m}匹配其前面的字符至少出现n次,最多出现m次
1 | [root@localhost kali]# grep "sa\{1,3\}i" test_rule.txt |
字符截取命令#### cut字段提取命令```bash
[root@localhost]# cut [选项] 文件名]# vi student.txt
选项:
-f 列号: 提取第几列
-d分隔符: 按照指定分隔符分割列
#示例:
[root@localhost
ID Name PHP LINUX MYSQL AVERAGE
1 liming 82 95 86 87.66
2 sc 74 96 87 85.66
3 gao 99 83 93 91.66
[root@localhost]# cut -f 2 student.txt]# cut -f 2,4 student.txt #提取第2列和第4列
[root@localhost
[root@localhost]# cut -d “:” -f 1,3 /etc/passwd]# cat /etc/passwd | grep /bin/bash | grep -v root | cut -d “:” -f 1
[root@localhost
#cut命令的局限(不是制表符,是空格符,无法分辨)
[root@localhost]# df -h | grep “sda5” #查看分区使用状况]# df -h | cut -d “ “ -f 1,3 #查看分区使用状况
[root@localhost
1 | #### printf命令(awk中使用)```bash |
在awk命令的输出中支持print和printf命令
- print:print会在每个输出之后自动加入一个换行符(Linux默认没有print命令)
- printf:printf是标准格式输出命令,并不会自动加入换行符,如果需要换行,需要手工加入换行符
awk命令比cut强大,awk是复杂的命令,可以经常编程,流程控制等
1 | # awk '条件1{动作1} 条件2{动作2}...' 文件名 |
注意:awk先读入第一行在开始处理
1 | #示例: |
sed命令sed是一种几乎包括在所有UNIX平台(包括Linux)的轻量级流编辑器。sed主要是用来将数据进行选取、替换、删除、新增的命令。
1 | [root@localhost~]# sed [选项] '[动作]' 文件名 |
1 | 示例:sed输出都不影响文件本身,只是影响屏幕输出 |
字符处理命令#### 排序命令sort```bash
[root@localhost ~]#sort [选项] 文件名
选项:
-f: 忽略大小写
-n: 以数值型进行排序,默认使用字符串型排序
-r: 反向排序
-t: 指定分隔符,默认分隔符是制表符
-k n[,m] 按照指定的字段范围排序。从第n字段开始,m字段结束(默认到行尾)
[root@localhost ~]#sort /etc/passwd #排序用户信息文件
[root@localhost ~]#sort -r /etc/passwd #反向排序
[root@localhost ~]#sort -t “:” -k 3,3 /etc/passwd #指定分隔符是“:”,用第三字段开头,第三字段结尾排序,就是只用第三字段排序。
[root@localhost ~]#sort -n -t “;” -k 3,3 /etc/passwd
1 | #### 统计命令wc```bash |
条件判断#### 安装文件类型进行判断| 测试选项 | 作用 |
| — | — |
| -b 文件 | 判断该文件是否存在,并且是否为块设备文件(是块设备文件为真) |
| -c 文件 | 判断该文件是否存在,并且是否为字符设备文件(是字符设备文件为真) |
| -d 文件 | 判断该文件是否存在,并且是否为目录文件(是目录为真) |
| -e 文件 | 判断该文件是否存在(存在为真) |
| -f 文件 | 判断该文件是否存在,并且是否为普通文件(是普通文件为真) |
| -L 文件 | 判断该文件是否存在,并且是否为符号链接文件(是符号链接文件为真) |
| -p 文件 | 判断该文件是否存在,并且是否为管道文件(是管道文件为真) |
| -s 文件 | 判断该文件是否存在,并且是否为非空(非空为真) |
| -s 文件 | 判断该文件是否存在,并且是否为套接字文件(是套接字文件为真) |
1 | # 两种判断格式(给程序看的) |
按照文件权限进行判断| 测试选项 | 作用 |
| — | — |
| -r 文件 | 判断该文件是否存在,并且是否该文件拥有读权限(有读权限为真) |
| -w 文件 | 判断该文件是否存在,并且是否该文件拥有写权限(有写权限为真) |
| -x 文件 | 判断该文件是否存在,并且是否该文件拥有执行权限(有执行权限为真) |
| -u 文件 | 判断该文件是否存在,并且是否该文件拥有SUID权限(有SUID权限为真) |
| -g 文件 | 判断该文件是否存在,并且是否该文件拥有SGID权限(有SGID权限为真) |
| -k 文件 | 判断该文件是否存在,并且是否该文件拥有SBit权限(有SBit权限为真) |
1 | [root@localhost ~]# [ -w /root/kali/test_rule.txt ]&& echo "yes"||echo "no" |
两个文件之间进行比较| 测试选项 | 作用 |
| — | — |
| 文件1 -nt 文件2 | 判断文件1的修改时间是否比文件2的新(如果新则为真) |
| 文件1 -ot 文件2 | 判断文件1的修改时间是否比文件2的旧(如果旧则为真) |
| 文件1 -ef 文件2 | 判断文件1是否和文件2的inode号一致,可以理解为两个文件是否为同一个文件,这个判断用于判断硬链接是很好的方法 |
两个整数之间比较(针对程序脚本的)| 测试选项 | 作用 |
| — | — |
| 整数1 -eq 整数2 | 判断整数1是否和整数2相等(相等为真) |
| 整数1 -ne 整数2 | 判断整数1是否和整数2不相等(不相等为真) |
| 整数1 -gt 整数2 | 判断整数1是否大于整数2(大于为真) |
| 整数1 -lt 整数2 | 判断整数1是否小于整数2(小于为真) |
| 整数1 -ge 整数2 | 判断整数1是否大于等于整数2(大于等于为真) |
| 整数1 -le 整数2 | 判断整数1是否小于等于整数2(小于等于为真) |
1 | # 判断整数23大于整数22 |
字符串的判断(常用的)| 测试选项 | 作用 |
| — | — |
| -z 字符串 | 判断字符串是否为空(为空返回真) |
| -n 字符串 | 判断字符串是否为非空(非空返回真) |
| 字串1==字串2 | 判断字符串1是否和字符串2相等(相等返回真) |
| 字串1!=字串2 | 判断字符串1是否和字符串2不相等(不相等返回真) |
1 | # 给name变量赋值 |
多重条件判断| 测试选项 | 作用 |
| — | — |
| 判断1 -a 判断2 | 逻辑与,判断1和判断2都成立,最终的结果为真 |
| 判断1 -o 判断2 | 逻辑或,判断1和判断2有一个成立,最终的结果就为真 |
| !判断 | 逻辑非,使原始的判断式取反 |
1 | [root@localhost ~]# aa=11 |
Shell流程控制### IF语句#### 单分支if条件语句```bash
if [条件判断式] ;then
程序
fi
或者
if [条件判断式]
then
程序
fi
1 | **单分支条件语句需要注意几个点:** |
双分支if条件语句```bash
if [条件判断式]
then
条件成立时,执行的程序
else
条件不成立时,执行的另一个程序
fi
1 | ```bash 备份mysql数据库 |
1 |
|
多分支if条件语句```bash
if [条件判断式1]
then
当条件判断式1成立时,执行程序1
elif [条件判断式2]
then
但条件判断式2成立时,执行程序2
…省略更多条件…
else
当所有条件都不成立时,最后执行此程序
fi
1 | ```bash 判断用户输入的是什么文件 |
CASE语句#### 多分支case条件语句(判断)case语句和if…elif…else语句一样都是多分支条件语句,不过和if多分支条件语句不同的是,case语句只能判断一种条件关系,而if语句可以判断多种条件关系。
1 | case $变量名 in |
1 | 示例1: |
FOR循环```bash 语法一
for 变量 in 值1 值2 值3…
do
程序
done
1 | ```bash |
1 | for ((初始值;循环控制条件;变量变化)) |
1 |
|
WHILE循环和UNTIL循环#### while循环while循环是不定循环,也称作条件循环。只要条件判断式成立,循环就会一直继续,直到条件判断式不成立,循环才会停止。这就是和for的固定循环不太一样了。
1 | while [ 条件判断式 ] |
1 |
|
until循环until循环,和while循环相反,until循环时只要条件判断式不成立则进行循环,并执行循环程序。一旦循环条件成立,则终止循环。
1 | until [ 条件判断式 ] |
1 |
|
Shell编程案例分析### 移动目录```bash
#!/bin/bash
编写shell脚本,把/root/目录下的所有目录(只需要一级)拷贝到/tmp/目录下;
cd /root/
for file in ls
do
if [ -d $file ]
then
mkdir /tmp/$file
else
continue
fi
done
1 | ### 输入整数```bash |
批量建立用户```bash
#!/bin/bash
批量建立用户user_00,user_01,…user_100并且所有用户同属于users组;
for i in seq 0 1 100
do
if [ $i -lt 10 ]
then
useradd -g 100 user_0$i
elif [ $i == 100 ]
then
useradd -g 100 user_100
else
useradd -g 100 user_$i
fi
done
1 | ### 计算1-100的和```bash |
获取随机数```bash
#!/bin/bash
RANDOM随机函数,100取余就可以获得1-100的随机整数
n=$[$RANDOM%100]
while :
do
read -p “请输入一个1-100间的整数:” n1
n2=echo $n1|sed 's/[0-9]//g'
if [ ! -z $n2 ]
then
echo “你输入的不是1-100的整数!”
continue
fi
if [ $n1 -gt 100 ] || [ $n1 == 0 ]
then
echo “请输入1-100的整数!”
continue
fi
if [ $n1 == $n ]
then
echo “你猜对了!”
break
elif [ $n1 -gt $n ]
then
echo “你输入的数字太大了!”
continue
else
echo “你输入的数字太小了!”
continue
fi
done
1 | ### 乘法口诀表```bash |
俄罗斯方块```bash
#!/bin/bash
Tetris Game
10.21.2003 xhchen<[email]xhchen@winbond.com.tw[/email]>
#APP declaration
APP_NAME=”${0##*[\/]}”
APP_VERSION=”1.0”
颜色定义
cRed=1
cGreen=2
cYellow=3
cBlue=4
cFuchsia=5
cCyan=6
cWhite=7
colorTable=($cRed $cGreen $cYellow $cBlue $cFuchsia $cCyan $cWhite)
位置和大小
iLeft=3
iTop=2
((iTrayLeft = iLeft + 2))
((iTrayTop = iTop + 1))
((iTrayWidth = 10))
((iTrayHeight = 15))
颜色设置
cBorder=$cGreen
cScore=$cFuchsia
cScoreValue=$cCyan
控制信号
改游戏使用两个进程,一个用于接收输入,一个用于游戏流程和显示界面;
当前者接收到上下左右等按键时,通过向后者发送signal的方式通知后者。
sigRotate=25
sigLeft=26
sigRight=27
sigDown=28
sigAllDown=29
sigExit=30
七中不同的方块的定义
通过旋转,每种方块的显示的样式可能有几种
box0=(0 0 0 1 1 0 1 1)
box1=(0 2 1 2 2 2 3 2 1 0 1 1 1 2 1 3)
box2=(0 0 0 1 1 1 1 2 0 1 1 0 1 1 2 0)
box3=(0 1 0 2 1 0 1 1 0 0 1 0 1 1 2 1)
box4=(0 1 0 2 1 1 2 1 1 0 1 1 1 2 2 2 0 1 1 1 2 0 2 1 0 0 1 0 1 1 1 2)
box5=(0 1 1 1 2 1 2 2 1 0 1 1 1 2 2 0 0 0 0 1 1 1 2 1 0 2 1 0 1 1 1 2)
box6=(0 1 1 1 1 2 2 1 1 0 1 1 1 2 2 1 0 1 1 0 1 1 2 1 0 1 1 0 1 1 1 2)
所有其中方块的定义都放到box变量中
box=(${box0[@]} ${box1[@]} ${box2[@]} ${box3[@]} ${box4[@]} ${box5[@]} ${box6[@]})
各种方块旋转后可能的样式数目
countBox=(1 2 2 2 4 4 4)
各种方块再box数组中的偏移
offsetBox=(0 1 3 5 7 11 15)
每提高一个速度级需要积累的分数
iScoreEachLevel=50 # be greater than 7
运行时数据
sig=0 # 接收到的signal
iScore=0 # 总分
iLevel=0 # 速度级
boxNew=() # 新下落的方块的位置定义
cBoxNew=0 # 新下落的方块的颜色
iBoxNewType=0 # 新下落的方块的种类
iBoxNewRotate=0 # 新下落的方块的旋转角度
boxCur=() # 当前方块的位置定义
cBoxCur=0 # 当前方块的颜色
iBoxCurType=0 # 当前方块的种类
iBoxCurRotate=0 # 当前方块的旋转角度
boxCurX=-1 # 当前方块的x坐标位置
boxCurY=-1 # 当前方块的y坐标位置
iMap=() # 背景方块图表
初始化所有背景方块为-1, 表示没有方块
for ((i = 0; i < iTrayHeight * iTrayWidth; i++)); do iMap[$i]=-1; done
接收输入的进程的主函数
function RunAsKeyReceiver()
{
local pidDisplayer key aKey sig cESC sTTY
pidDisplayer=$1
aKey=(0 0 0)
cESC=echo -ne "\033"
cSpace=echo -ne "\040"
保存终端属性。在read -s读取终端键时,终端的属性会被暂时改变。
如果在read -s时程序被不幸杀掉,可能会导致终端混乱,
需要在程序退出时恢复终端属性。
sTTY=stty -g
捕捉退出信号
trap “MyExit;” INT TERM
trap “MyExitNoSub;” $sigExit
隐藏光标
echo -ne “\033[?25l”
while :
do
读取输入。注-s不回显,-n读到一个字符立即返回
read -s -n 1 key
aKey[0]=${aKey[1]}
aKey[1]=${aKey[2]}
aKey[2]=$key
sig=0
判断输入了何种键
if [[ $key == $cESC && ${aKey[1]} == $cESC ]]
then
ESC键
MyExit
elif [[ ${aKey[0]} == $cESC && ${aKey[1]} == “[“ ]]
then
if [[ $key == “A” ]]; then sig=$sigRotate # <向上键>
elif [[ $key == “B” ]]; then sig=$sigDown # <向下键>
elif [[ $key == “D” ]]; then sig=$sigLeft # <向左键>
elif [[ $key == “C” ]]; then sig=$sigRight # <向右键>
fi
elif [[ $key == “W” || $key == “w” ]]; then sig=$sigRotate # W, w
elif [[ $key == “S” || $key == “s” ]]; then sig=$sigDown # S, s
elif [[ $key == “A” || $key == “a” ]]; then sig=$sigLeft # A, a
elif [[ $key == “D” || $key == “d” ]]; then sig=$sigRight # D, d
elif [[ “[$key]” == “[]” ]]; then sig=$sigAllDown # 空格键
elif [[ $key == “Q” || $key == “q” ]] # Q, q
then
MyExit
fi
if [[ $sig != 0 ]]
then
向另一进程发送消息
kill -$sig $pidDisplayer
fi
done
}
退出前的恢复
function MyExitNoSub()
{
local y
恢复终端属性
stty $sTTY
((y = iTop + iTrayHeight + 4))
显示光标
echo -e “\033[?25h\033[${y};0H”
exit
}
function MyExit()
{
通知显示进程需要退出
kill -$sigExit $pidDisplayer
MyExitNoSub
}
处理显示和游戏流程的主函数
function RunAsDisplayer()
{
local sigThis
InitDraw
挂载各种信号的处理函数
trap “sig=$sigRotate;” $sigRotate
trap “sig=$sigLeft;” $sigLeft
trap “sig=$sigRight;” $sigRight
trap “sig=$sigDown;” $sigDown
trap “sig=$sigAllDown;” $sigAllDown
trap “ShowExit;” $sigExit
while :
do
根据当前的速度级iLevel不同,设定相应的循环的次数
for ((i = 0; i < 21 - iLevel; i++))
do
sleep 0.02
sigThis=$sig
sig=0
根据sig变量判断是否接受到相应的信号
if ((sigThis == sigRotate)); then BoxRotate; # 旋转
elif ((sigThis == sigLeft)); then BoxLeft; # 左移一列
elif ((sigThis == sigRight)); then BoxRight; # 右移一列
elif ((sigThis == sigDown)); then BoxDown; # 下落一行
elif ((sigThis == sigAllDown)); then BoxAllDown; # 下落到底
fi
done
kill -$sigDown $$
BoxDown # 下落一行
done
}
BoxMove(y, x), 测试是否可以把移动中的方块移到(x, y)的位置, 返回0则可以, 1不可以
function BoxMove()
{
local j i x y xTest yTest
yTest=$1
xTest=$2
for ((j = 0; j < 8; j += 2))
do
((i = j + 1))
((y = ${boxCur[$j]} + yTest))
((x = ${boxCur[$i]} + xTest))
if (( y < 0 || y >= iTrayHeight || x < 0 || x >= iTrayWidth))
then
撞到墙壁了
return 1
fi
if ((${iMap[y * iTrayWidth + x]} != -1 ))
then
撞到其他已经存在的方块了
return 1
fi
done
return 0;
}
将当前移动中的方块放到背景方块中去,
并计算新的分数和速度级。(即一次方块落到底部)
function Box2Map()
{
local j i x y xp yp line
将当前移动中的方块放到背景方块中去
for ((j = 0; j < 8; j += 2))
do
((i = j + 1))
((y = ${boxCur[$j]} + boxCurY))
((x = ${boxCur[$i]} + boxCurX))
((i = y * iTrayWidth + x))
iMap[$i]=$cBoxCur
done
消去可被消去的行
line=0
for ((j = 0; j < iTrayWidth * iTrayHeight; j += iTrayWidth))
do
for ((i = j + iTrayWidth - 1; i >= j; i–))
do
if ((${iMap[$i]} == -1)); then break; fi
done
if ((i >= j)); then continue; fi
((line++))
for ((i = j - 1; i >= 0; i–))
do
((x = i + iTrayWidth))
iMap[$x]=${iMap[$i]}
done
for ((i = 0; i < iTrayWidth; i++))
do
iMap[$i]=-1
done
done
if ((line == 0)); then return; fi
根据消去的行数line计算分数和速度级
((x = iLeft + iTrayWidth * 2 + 7))
((y = iTop + 11))
((iScore += line * 2 - 1))
显示新的分数
echo -ne “\033[1m\033[3${cScoreValue}m\033[${y};${x}H${iScore} “
if ((iScore % iScoreEachLevel < line * 2 - 1))
then
if ((iLevel < 20))
then
((iLevel++))
((y = iTop + 14))
显示新的速度级
echo -ne “\033[3${cScoreValue}m\033[${y};${x}H${iLevel} “
fi
fi
echo -ne “\033[0m”
重新显示背景方块
for ((y = 0; y < iTrayHeight; y++))
do
((yp = y + iTrayTop + 1))
((xp = iTrayLeft + 1))
((i = y * iTrayWidth))
echo -ne “\033[${yp};${xp}H”
for ((x = 0; x < iTrayWidth; x++))
do
((j = i + x))
if ((${iMap[$j]} == -1))
then
echo -ne “ “
else
echo -ne “\033[1m\033[7m\033[3${iMap[$j]}m\033[4${iMap[$j]}m[]\033[0m”
fi
done
done
}
下落一行
function BoxDown()
{
local y s
((y = boxCurY + 1)) # 新的y坐标
if BoxMove $y $boxCurX # 测试是否可以下落一行
then
s=”DrawCurBox 0“ # 将旧的方块抹去
((boxCurY = y))
s=”$sDrawCurBox 1“ # 显示新的下落后方块
echo -ne $s
else
走到这儿, 如果不能下落了
Box2Map # 将当前移动中的方块贴到背景方块中
RandomBox # 产生新的方块
fi
}
左移一列
function BoxLeft()
{
local x s
((x = boxCurX - 1))
if BoxMove $boxCurY $x
then
s=DrawCurBox 0
((boxCurX = x))
s=$sDrawCurBox 1
echo -ne $s
fi
}
右移一列
function BoxRight()
{
local x s
((x = boxCurX + 1))
if BoxMove $boxCurY $x
then
s=DrawCurBox 0
((boxCurX = x))
s=$sDrawCurBox 1
echo -ne $s
fi
}
下落到底
function BoxAllDown()
{
local k j i x y iDown s
iDown=$iTrayHeight
计算一共需要下落多少行
for ((j = 0; j < 8; j += 2))
do
((i = j + 1))
((y = ${boxCur[$j]} + boxCurY))
((x = ${boxCur[$i]} + boxCurX))
for ((k = y + 1; k < iTrayHeight; k++))
do
((i = k * iTrayWidth + x))
if (( ${iMap[$i]} != -1)); then break; fi
done
((k -= y + 1))
if (( $iDown > $k )); then iDown=$k; fi
done
s=DrawCurBox 0 # 将旧的方块抹去
((boxCurY += iDown))
s=$sDrawCurBox 1 # 显示新的下落后的方块
echo -ne $s
Box2Map # 将当前移动中的方块贴到背景方块中
RandomBox # 产生新的方块
}
旋转方块
function BoxRotate()
{
local iCount iTestRotate boxTest j i s
iCount=${countBox[$iBoxCurType]} # 当前的方块经旋转可以产生的样式的数目
计算旋转后的新的样式
((iTestRotate = iBoxCurRotate + 1))
if ((iTestRotate >= iCount))
then
((iTestRotate = 0))
fi
更新到新的样式, 保存老的样式(但不显示)
for ((j = 0, i = (${offsetBox[$iBoxCurType]} + $iTestRotate) * 8; j < 8; j++, i++))
do
boxTest[$j]=${boxCur[$j]}
boxCur[$j]=${box[$i]}
done
if BoxMove $boxCurY $boxCurX # 测试旋转后是否有空间放的下
then
抹去旧的方块
for ((j = 0; j < 8; j++))
do
boxCur[$j]=${boxTest[$j]}
done
s=DrawCurBox 0
画上新的方块
for ((j = 0, i = (${offsetBox[$iBoxCurType]} + $iTestRotate) * 8; j < 8; j++, i++))
do
boxCur[$j]=${box[$i]}
done
s=$sDrawCurBox 1
echo -ne $s
iBoxCurRotate=$iTestRotate
else
不能旋转,还是继续使用老的样式
for ((j = 0; j < 8; j++))
do
boxCur[$j]=${boxTest[$j]}
done
fi
}
DrawCurBox(bDraw), 绘制当前移动中的方块, bDraw为1, 画上, bDraw为0, 抹去方块。
function DrawCurBox()
{
local i j t bDraw sBox s
bDraw=$1
s=””
if (( bDraw == 0 ))
then
sBox=”\040\040”
else
sBox=”[]”
s=$s”\033[1m\033[7m\033[3${cBoxCur}m\033[4${cBoxCur}m”
fi
for ((j = 0; j < 8; j += 2))
do
((i = iTrayTop + 1 + ${boxCur[$j]} + boxCurY))
((t = iTrayLeft + 1 + 2 * (boxCurX + ${boxCur[$j + 1]})))
\033[y;xH, 光标到(x, y)处
s=$s”\033[${i};${t}H${sBox}”
done
s=$s”\033[0m”
echo -n $s
}
更新新的方块
function RandomBox()
{
local i j t
更新当前移动的方块
iBoxCurType=${iBoxNewType}
iBoxCurRotate=${iBoxNewRotate}
cBoxCur=${cBoxNew}
for ((j = 0; j < $