很多人写代码代码写着写着就放弃了,我就是其中一个,因为太枯燥太无聊太费脑子了,感觉掉两根头发都对不起我的父母。以前写java的时候感觉写代码是世界上最难的事情,但是随着工作经验的增长慢慢开始对编程有了新的理解和看法,以前看不懂的线程、堆栈啥的回过头在看看发现竟然不是那么难理解了,所以才有了我要拿下shell编程的想法,其实说白了是工作需要,哈哈!
1.shell的执行方式
#第一个脚本
vim hello.sh
#!/bin/Bash
echo -e "11111"
第二步:赋予权限
chmod 755 hello.sh
第三步:通过bash或者输入路径调用执行
bash hello.sh #此方法可以忽略赋予权限
./hello/sh #可以通过相对路径或者直接路径的方式调用
2.bash的基本功能
2.1历史命令
history [-c-w] [历史命令保存文件]
-c 清空历史命令
-w 把缓存中的历史命令写入历史命令保存文件 ~/.bash_history
#历史命令默认保存1000条,可以修改环境变量配置文件/etc/profile
!n重复执行第n条历史命令
!!重复执行上一条命令
!字串 重复执行最后一条以该字串开头的命令
2.2命令别名与常用快捷键
1.命令别名
alias #查询命令别名
alias 别名='原命令' #设定命令别名
让别名永久生效
vim /root/./bashrc #在每个用户的家目录下
删除别名
unalias 别名
2.常用快捷键
ctrl+A 把光标移动到命令行开头
ctrl+E 把光标移动到命令行结尾
ctrl+C 强制终止当前的命令
ctrl+L 清屏,相当于clear命令
ctrl+U 删除或剪切光标之前的命令
ctrl+K 删除或剪切光标之后的内容
ctrl+Y 粘贴ctrl+U或ctul+K剪切的内容
ctrl+R 在历史命令中搜索
ctrl+D 退出当前终端
ctrl+Z 暂停,并放入后台
ctrl+S 暂停屏幕输出
ctrl+Q 恢复屏幕输出
2.3输入输出重定向
1.输出重定向:执行完命令把本应该输出到屏幕上的结果输出到文件中或者其他地方
命令 > 文件 覆盖文件
命令 >> 文件 追加到文件里
错误命令 2> 文件 覆盖文件
错误命令 2>> 文件 追加到文件里
命令 > 文件 2>!&1 以覆盖的方式将正确和错误输出都保存到同一文件中
命令 >> 文件 2>!&1 以追加的方式将正确和错误输出都保存到同一文件中
命令 &> 文件 以覆盖的方式将正确和错误输出都保存到同一文件中
命令 &>> 文件 以追加的方式将正确和错误输出都保存到同一文件中
命令 >> 文件1 2>>文件2 把正确的输出追加到文件1,错误的追加到文件2
2.输入重定向:把原本应该由键盘输入的内容通过文件来输入
2.4多命令顺序执行与管道符
1.; 命令1;命令2;...命令n 2个命令依次执行
例如:
[root@MiWiFi-R3L-srv ~]# ls;date;pwd
anaconda-ks.cfg ntfs-3g_ntfsprogs-2017.3.23.tgz 下载 文档 视频
initial-setup-ks.cfg vm.tar.gz 公共 桌面 音乐
ntfs-3g_ntfsprogs-2017.3.23 vmware-tools-distrib 图片 模板
Fri 28 Feb 07:02:46 EST 2020
/root
例如2
dd [-if=输入文件-of=输出文件-bs=字节数-count=个数] #主要进行磁盘对刻
if=输入文件 指定源文件或源设备
of=输出文件 指定目标文件或目标设备
bs=字节数 指定一次输入/输出多少字节
count=个数 指定输入/输出多少个字节
例如:date;dd if=/dev/zero of=/root/testfile bs=1k count=100000;date
2.&& 命令1&&命令2...命令n 命令1正确执行后命令2才会执行,命令1执行错误,命令2不执行
例如:./configure && make &&make install #当编译成功才会安装
3.|| 命令1||命令2...命令n 命令1执行不正确2才会执行,当命令1正确执行后命令2不执行
4.管道符
命令1 | 命令2 #把1的正确输出作为2的输入(作为2的操作对象)
常见用法:
[root@MiWiFi-R3L-srv ~]# netstat -an | grep tcp
tcp 0 0 192.168.122.1:53 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN
tcp6 0 0 :::22 :::* LISTEN
tcp6 0 0 ::1:631 :::* LISTEN
tcp6 0 0 :::111 :::* LISTEN
2.5通配符与其他特殊符号
? 匹配一个任意字符
* 匹配0个或多个任意字符
[] 匹配括号中任意一个字符 [abc]匹配abc中的一个
[-] 匹配括号中任意一个字符 [a-z]匹配a-z中的一个
[^] 逻辑非,匹配不是括号中的字符 [^0-9]匹配一个不是0数字的字符
'' 单引号括起来的字符都是普通字符
"" 双引号括起来的字符都是普通字符,"$"和"\"和反引号是拥有特殊含义的,"$"代表引用变量的值,如果需要在双引号中输出$和反引号需要在符号前加上转义符\
`` 反引号括起来的是系统命令
$() 和反引号一样
# #开头代表注释
$ 变量
\ 转义符 跟在\之后的特殊符号将变为普通字符
3.bash变量
3.1用户自定义变量:自己随意定义
[localhost@MiWiFi-R3L-srv ~]$ name="123" #变量赋值
[localhost@MiWiFi-R3L-srv ~]$ echo $name #变量调用
123
[localhost@MiWiFi-R3L-srv ~]$ aa=123
[localhost@MiWiFi-R3L-srv ~]$ aa="$aa"456 #变量叠加
[localhost@MiWiFi-R3L-srv ~]$ echo $aa #变量调用
123456
set #查看所有变量
unset name #删除刚才设置的变量name
3.2环境变量:主要保存和系统操作环境相关的数据,可以添加新的值,变量的作用范围是当前声明变量的shell以及所有子shell(pstree)
export 变量名=变量值 #申明变量
env #专门查询环境变量
unset #删除变量
系统常见环境变量:
1.PATH:系统查找命令的路径
[localhost@MiWiFi-R3L-srv ~]$ echo $PATH
/home/localhost/.local/bin:/home/localhost/bin:/home/localhost/.local/bin:/home/localhost/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin
PATH="PATH":/root/sh #PATH变量叠加
2.PS1:定义系统提示符的变量
\d:显示日期
\h:显示主机名
\t:显示24小时制时间,HH:MM:SS
\T:显示12小时制时间,HH:MM:SS
\A:显示24小时制时间,HH:MM
\u:显示当前用户名
\w:显示当前所在目录的完整名称
\W:显示当前所在目录的最后一个目录
\#:执行的第几个命令
\$:提示符。如果root显示为#普通用户显示为$
[localhost@MiWiFi-R3L-srv ~]$ echo $PS1
[\u@\h \W]\$ #提示符的默认格式
\u对应显示用户名localhost
@对应自己本身
\h对应主机名MiWiFi-R3L-srv
3.3位置参数变量:用来向脚本当中传递参数,变量名不能自定义,变量作用是固定的
$n:n为数字,$0代表命令本身,$1-$9代表第一到第九个参数,十以上的参数需要用大括号包含,${10}
$*:代表命令行中所有的参数,$*把所有的参数看做一个整体
$@: 代表命令行中所以的参数,不过$@会把每个参数区分对待
$#: 代表命令行中所有的参数的个数
3.4预定义变量:是Bash中已经定义好的变量,变量名不能自定义,变量作用也是固定的
$?:最后一次执行的命令的返回状态,如果这个变量的值为0,证明上一个命令正确执行,如果这个变量的值非0,则证明上一个命令执行不正确
$$:当前进程的进程号PID
$!:后台运行的最后一个进程的进程号
接收键盘输入
read [-p-t-n-s][变量名]
-p 提示信息,在等待read时候输出提示项可信心
-t 秒数,read会一直等待用户输入,此选以指定等待时间
-n 字符数,read只接受指定的字符数
-s 隐藏输入的数据
4.bash运算符
1.数值运算与运算符
declare [+-/][-i-x-p] 变量名 #声明变量类型
- 给变量设定类型属性
+ 取消变量的类型属性
-i 将变量声明为整数型 (integer)
-x 将变量声明为环境变量
-p 显示指定变量的被声明的类型
例如:
[root@MiWiFi-R3L-srv sh]# aa=11
[root@MiWiFi-R3L-srv sh]# bb=22
[root@MiWiFi-R3L-srv sh]# declare -i cc=$aa+$bb #将cc用i参数声明为整数型
[root@MiWiFi-R3L-srv sh]# echo $cc
33
例如:
[root@MiWiFi-R3L-srv sh]# dd=$(expr $aa + $bb) #+左右必须加空格
[root@MiWiFi-R3L-srv sh]# echo $dd
33
例如:
[root@MiWiFi-R3L-srv sh]# ff=$(($aa+$bb)) #推荐
[root@MiWiFi-R3L-srv sh]# gg=$[$aa+$bb]
[root@MiWiFi-R3L-srv sh]# echo $ff
33
[root@MiWiFi-R3L-srv sh]# echo $gg
33
2.变量测试与内容替换
优先级 | 运算符 | 说明 |
13 | - + | 单目负 单目正 |
12 | ! ~ | 逻辑非 按位取反或补码 |
11 | * / % | 乘 除 取模 |
10 | + - | 加 减 |
9 | << >> | 按位左移 按位右移 |
8 | < = >= < > | 小于或等于 大于或等于 大于小于 |
7 | == != | 等于 不等于 |
6 | & | 按位与 |
5 | ^ | 按位异或 |
4 | | | 按位或 |
3 | && | 逻辑与 |
2 | || | 逻辑或 |
1 | = += - = *= /= &= ^= |= <<= >>= | 赋值且运算 |
5.环境变量配置文件
source 配置文件 或 . 空格 配置文件 #让配置文件直接生效
1.配置文件作用,图片见下面
开机先运行/etc/profile,其中此文件定义了USER:LOGNAME:MAIL:PATH:HOSTNAME:HISTSIZE:umask:
完了再调用/etc/profile.d/*.sh,其中对主要的是调用/etc/profile/lang.sh,lang.sh又调用了/etc/locate.conf
,locate.conf定义了LANG=zh_CN.UTF-8,接下来调用家目录下的bash_profile,bash_profile又执行了家目录下的
~/.bashrc,其中~/.bashrc又重新定义了PATH,PATH="$HOME/.local/bin:$HOME/bin:$PATH" export PATH,还将其设置为
环境变量,接下来又调用了/etc/bashrc,其中也是重新定义了umask,PS1,PATH等的值,最后返回提示符界面
注意:后面定义的内容可以覆盖前面定义的内容
/etc/bashrc用于no login
/etc/profile是需要login的
2.其他配置文件和登录信息
~/.bash_logout #注销配置文件,默认为空
~/.bash_history #历史命令配置文件,不推荐清空
/etc/issue #定义本机登录终端登录前欢迎信息
\d 显示当前系统时间
\s 显示操作系统名称
\l 显示登录的终端号
\m 显示硬件结构.i386 i686
\n 显示主机名
\o 显示域名
\r 显示内核版本
\t 显示当前系统时间
\u 显示当前登录用户的序列号
/etc/issue.net #定义远程终端登录前欢迎信息
是否显示是定义了/etc/ssh/sshd_config
#Banner none
Banner /etc/issue.net #在Banner行下添加Banner /etc/issue.net行
重启ssh服务service sshd restart
/etc/motd #定义本机和远程登录后的欢迎信息
6.正则表达式
6.1基础正则,略。
6.2字符截取命令
cut
cut #提取符号条件的列
cut [-f-d] 文件名
-f 列号 提取第几列
-d 分隔符 按照文件中的指定分隔符分割列
注意局限性:cut命令的分割符不能是空格
例如:
[localhost@kali tmp]$ cut -f 1,2,3 student.txt
id name age
1 aa 15
2 job 16
3 jack 19
4 sb 17
printf
printf [输出类型输出格式] 输出内容
输出类型:
%s 输出字符串 可以输出多个,多个需要用单引号括起来
%i 输出整数 可以输出多个
%m.nf 输出浮点数,mn是数字,指输出的整数位数和小数位数,%8.2f指输出8位 2位小数 6位整数
输出格式:
\a 警告声音
\b 退格键
\f 清楚屏幕
\n 换行
\r 回车
\t 水平输出退格键
\v 垂直输出退格键
例如:
[localhost@kali tmp]$ printf %s 1 2 3
123
[localhost@kali tmp]$ printf '%s \n' 1 2 3
1
2
3
[localhost@kali tmp]$ printf '%s %s \n' 1 2 3 4 5 6
1 2
3 4
5 6
[localhost@kali tmp]$ printf '%s\t %s\t %s\t %s\t \n' $(cat student.txt)
id name age chengji
1 aa 15 99
2 job 16 91
3 jack 19 95
4 sb 17 95
awk
awk '条件1{动作1}条件2{动作2}...' 文件名
条件:
一般使用关系表达式作为条件
x>10
x>=10
x<=10
动作:
格式化输出
流程控制语句
awk '{print $1}'
sed #是一种轻量级流编辑器
sed [-n -e -i] '[动作]' 文件名
-n 默认输出所有,加入n会把经过此参数处理的行输出到屏幕
-e 允许对输入数据应用多条sed命令编辑
-i 用sed修改结果直接修改读取数据的文件 而不是屏幕输出
动作:
a\ 追加,在当前行后添加一行或多行,添加多行时,除最后一行外每行末尾需要用\代表数据完结
c\ 行替换 用c后面的字符替换原数据行 替换多行时,用\代替完结
i\ 插入 在当前行前插入一行或多行 替换多行时,用\代替完结
d 删除 删除指定的行
p 打印 输出指定的行
s 字串替换 用一个字符串替换另外一个字符串 格式 "行范围s/旧字串/新字串/g"
例如:
[root@kali tmp]# sed '2p' -n student.txt #只输出第二行
1 aa 15 99
[root@kali tmp]# cat student.txt
id name age chengji
1 aa 15 99
2 job 16 91
3 jack 19 95
4 sb 17 95
[root@kali tmp]# sed '1,2d' student.txt #删除 1 2行
2 job 16 91
3 jack 19 95
4 sb 17 95
[root@kali tmp]# sed '2a test' student.txt #在第二行后面追加
id name age chengji
1 aa 15 99
test
2 job 16 91
3 jack 19 95
4 sb 17 95
[root@kali tmp]# sed '1a aaaa' -i student.txt #在第一行前添加aaaa 并写入文件
[root@kali tmp]# cat student.txt
id name age chengji
aaaa
1 aa 15 99
2 job 16 91
3 jack 19 95
4 sb 17 95
[root@kali tmp]# sed -i '2s/aaaa/999/g' student.txt #在第二行中的aaaa替换成999并写入文件
[root@kali tmp]# cat student.txt
id name age chengji
999
1 aa 15 99
2 job 16 91
3 jack 19 95
4 sb 17 95
[root@kali tmp]# sed -e 's/job/test/g;s/jack/test1/g' student.txt
id name age chengji
999
1 aa 15 99
2 test 16 91 #同时替换job为test jack为test1
3 test1 19 95
4 sb 17 95
6.3字符处理命令
sort
sort [-f -n -r -t- k n[,m]] #排序命令
-f 忽略大小写
-n 以数值型进行排序 默认使用字符串型排序
-r 反向排序
-t 指定分隔符 默认分隔符是制表符
-k n[,m] 按照指定的字段范围排序 从第n字段开始 m字段结束
例如:
sort /etc/passwd
sort -r /etc/passwd
sort -n -t ":" -k 3,5 /etc/passwd #以:为分隔符 根据第三和第五行综合排序
wc
wc [-l -w -m] 文件名 #统计命令
-l 只统计行数
-w 只统计单词数
-m 只统计字符数
6.4条件判断
按照文件类型进行判断
-b 判断文件是否存在,并且是否为块设备文件
-c 判断文件是否存在,并且是否为字符设备文件
-d 判断文件是否存在,并且是否为目录文件 常用
-e 判断文件是否存在 存在为真 常用
-f 判断文件是否存在,并且是否为普通文件 常用
-L 判断文件是否存在,并且是否为符号链接文件
-p 判断文件是否存在,并且是否为管道文件
-s 判断文件是否存在,并且是否为空 非空为真
-S 判断文件是否存在,并且是否为套接字文件
例如:
[root@kali tmp]# test -e student.txt
[root@kali tmp]# echo $?
0
[root@kali ~]# test -n /root && echo "yes" || echo "no"
yes
[root@kali ~]# [ -e /root ] && echo "yes" || echo "no" #注意空格
yes
按照文件权限进行判断
-r 判断文件是否存在,并且是否该文件拥有读权限
-w 判断文件是否存在,并且是否该文件拥有写权限
-x 判断文件是否存在,并且是否该文件拥有执行权限
-u 判断文件是否存在,并且是否该文件拥有SUID权限
-g 判断文件是否存在,并且是否该文件拥有SGID权限
-k 判断文件是否存在,并且是否该文件拥有SBit权限
[root@kali ~]# ll /etc/passwd
-rw-r--r--. 1 root root 2496 2月 28 05:41 /etc/passwd
[root@kali ~]# [ -x /etc/passwd ] && 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
[root@kali tmp]# [ 3 -eq 2 ] && echo yes || echo no
no
[root@kali tmp]# [ 3 -gt 2 ] && echo yes || echo no
字符串判断
-z 字符串 判断字符串是否为空
-n 字符串 判断字符串是否非空
字串1 ==字串2 判断12字符串是否相等
字串1 !=字串2 判断12字符串是否不等
[root@kali ~]# a="test"
[root@kali ~]# [ -z $a ] && echo yes || echo no
no
[root@kali ~]# [ -n $a ] && echo yes || echo no
多重条件判断
判断1 -a 判断2 逻辑与,判断1和判断2都成立最终结果为真
判断1 -o 判断2 逻辑或,判断1和判断2任意一个成立,最终结果为真
! 判断 逻辑非,使原始的判断取反
[root@kali ~]# name=1
[root@kali ~]# age=18
[root@kali ~]# [ $name=1 -a $age -gt 15 ] && echo yes || echo no
6.5流程控制
单分支if条件语句
语法:
if [条件判断] ;then
程序体
fi
或者
if [条件判断]
then
程序
fi
例如:
#!/bin/bash
#磁盘容量使用超过14报警
aa=$(df -h | grep /dev/mapper/cl-root | awk '{print $5}' | cut -d '%' -f1)
if [ $aa -ge 14 ]
then
echo '/dev/mapper/cl-root is full'
fi
双分支if条件语句
语法:
if [条件判断]
then
条件为真,执行程序1
else
条件为假,执行程序2
fi
例如:
#!/bin/bash
#备份/etc到/tmp/backup中
date=$(date +%y+%m+%d)
size=$(du -sh /etc)
if [ -d /tmp/backup ]
then
echo 'date is : $date' > /tmp/backup/db.txt
echo 'size is : $size' >> /tmp/backup/db.txt
cd /tmp/backup
tar -zcf etc_backup_$date.tar.gz db.txt /tmp &> /dev/null
rm -rf /tmp/backup/db.txt
else
mkdir /tmp/backup
echo 'date is : $date' > /tmp/backup/db.txt
echo 'size is : $size' >> /tmp/backup/db.txt
cd /tmp/backup
tar -zcf etc_backup_$date.tar.gz db.txt /tmp &> /dev/null
rm -rf /tmp/backup/db.txt
fi
多分支if条件语句
语法:
if [条件判断1]
then
条件为1,执行程序1
elif [条件判断2]
then
条件为2,执行程序2
elif [条件判断3]
then
条件为3,执行程序3
elif ...
then ...
else
条件都不是,最后执行程序3
fi
case语句
case $变量名 in
"参数1")
参数等于1,执行1
;;
"参数2")
参数等2,执行2
;;
...
*)
参数都不是,执行此
esac
例如:
#!/bin/bash
#case test
echo 'please input 1'
echo 'please input 2'
echo 'please input 3'
read -p 'please input 1,2,3' -t 30 ca
case $ca in
"1")
echo 1
;;
"2")
echo 2
;;
"3")
echo 3
;;
*)
echo 'input is wrong!!!'
esac
for
语法1:
for 变量 in 值1 值2 值3 ...
do
程序
done
例如
for a in 1 2 3
do
echo $a
done
例如2
#批量解压缩
cd /lamp
ls *.tar.gz >ls.log
for i in $(cat ls.log)
do
tar -zxf $i &> /dev/null
done
rm -rf /lamp/ls.log
语法2:
for ((初始值;循环控制条件;变量变化))
do
程序
done
例如:
#1+..+100
a=0
for ((i=1;i<=100;i=i+1))
do
a=$(($j+$i))
done
echo 'sum is':$a
while
注意:for循环的条件判断式直到执行完就终止,而while的条件判断式只要成立就一直循环
while [条件判断式]
do
程序
done
例如:
i=1
a=0
while [ $i -le 100] #-le小于等于100
do
a=$(( $a+$i))
i=$(( $i+1))
done
echo 'sum is :' $a
until
注意:和while循环相反,while只要条件成立就一直循环,until只要条件不成立才会循环,条件成立就终止
until [条件判断式]
do
程序
done
例如:
i=1
a=0
until [ $i -gt 100] #-gt 大于等于100
do
a=$(( $a+$i))
i=$(( $i+1))
done
echo 'sum is :' $a
就为那一个月多发的几千块钱也得努力呀同学们!!!