一、前言
什么是编程
编程就是人把自己想让计算机做的事情,用计算机
能听懂的语言(编程语言)给翻译下来
所以编程分为两个环节:
1、先想清楚做事的步骤
2、再用编程语言把做事步骤给翻译下来
为何要编程
为了让计算机能够按照人类的思维逻辑(人类编写程序)去工作,从而把人解放出来
什么是程序:
编程的结果就是程序,具体来说程序一个多个代码文件
整个程序就一个文件(实现功能相对简单、代码量少):称之为脚本
整个程序由很多文件夹组织了很多文件:称之为软件
什么是进程
进程指的是一个程序的运行过程,或者说一个正在运行的程序
编程语言分类 :
机器语言:用计算机能听懂的二进制指令去编写程序
优点:执行效率最高
缺点:开发效率最低
汇编语言:用英文标签代替二进制指令去写程序
优点:开发效率略高于机器语言(解决了机器语言二进制指令难以记忆的问题)
缺点:相比机器语言来说执行效率略低
总结:
虽然汇编语言是一种进步
但是用汇编语言开发程序的仍然比较复杂
高级语言:站在人能理解的表达方式去写程序,计算机无法直接理解
需要经过翻译才能被计算机理解执行,按照翻译方式的不同又分为两大类
1、编译型(c、go)
源代码-------编译器(类似于谷歌翻译)-----》可执行的二进制指令
特点:
拿到编译结果之后,第二次运行不需要再编译,直接拿着上次翻译的结果执行即可
2、解释型 (shell、python)
源代码-------解释器(类似于同声传译)-----》可执行的二进制指令
特点:
每次执行 都需要让解释器解释执行 (读一行解释一行然后执行)
开发效率:
高级语言 》 汇编语言》机器语言
执行效率:
机器语言》汇编语言》高级语言(编译型>解释型)
shell两层意思:
1、shell这门编程语言
2、shell解释器:专门负责解释执行shell这门语言的语法规则
shell解释器种类:
bash
sh

shell程序可以在两个地方写
交互式环境
优点:每敲一条命令理解得到结果,然后才能执行下一条命令
缺点:无法永久保存命令
写入文件中(脚本)
优点:永久保存命令、重复执行
缺点:无法单纯测试某条命令的运行结果
编写shell脚本的组成部分
#!/bin/bash -----------》指定由哪个解释器来解释执行当前文件的代码(默认就是bash )
注释部分:是对代码的解释说明
执行shell脚本由四种方式:
1、启动了一个子bash进程,即在子bash进程执行
1、绝对路径:/a/b/b.sh
权限:
1、对沿途 的文件夹都要有x
2、对目标文件 要有r与x
2、相对路径:
cd /a
b/b.sh
cd /a/b/
./b.sh ############################(./没有空格)
权限:
1、对沿途的文件夹都要有x
2、对目标文件要有r与x
3、bash解释器+脚本文件的路径
权限:
1、对沿途的文件夹都要有x
2、对目标文件要有r(本质执行的是bash命令,不用给目标文件执行权限也能执行)
2、在当前bash进程里执行
特点:
1、对沿途文件夹有x权限
2、对目标文件有r权限即可
source /a/b/b.sh
. /a/b/b.sh ##################################(. / 中间有空格)
注释:
1、对代码进行解释说明
大前提:只在关键代码上加注释
添加的位置:
1、代码的正上方单独一行
2、或者是代码的正后方(注意要加一个空格)
2、可以将暂时不想运行的代码给注释掉
程序的调试(debug):
sh -vx login.sh # 不加-v选项,只会显示程序中运行的代码,不会显示注释信息
sh -n login.sh # 只调试语法是否有问题
cat login.sh ###set -x set +x 只调试范围内的代码段
#!/usr/bin/env bash
set -x
read -p "请输入您的名字: " name
read -p "请输入您的密码: " pwd
set +x
if [[ "name" == "egon" \&\& "pwd" == "123" ]];then
echo "登录成功"
else
echo "账号或密码错误"
fi
. login.sh

二、变量
定义
变量本质是一种数据的存储机制,数据存放于内存中
为何要有变量
为了让计算机能够像一样去记住事物的状态,并且状态可以变化
程序=数据+功能
先定义
定义变量由三大部分构成
变量名=变量值 # 注意=左右两边不能有空格
age=18
ip="1.1.1.10"
msg="hello world"
后引用
echo $age
echo ${age}
percent=33
echo {percent}% # 注意:如果是打印百分比,建议使用{变量名}%
33%
删除
unset age
root@localhost shell\]# ip="192.168.11.10" **# 字符串类型加引号** \[root@localhost shell\]# echo $ip 192.168.11.10 定义一个变量由三大部分组成 变量名: 用来访问到变量值的 赋值符号=: 将变量值的内存地址绑定给变量名 变量值: 即我们存的数据 #**变量名** 的命令应该见名知意,同时遵循如下规则 以字母或下划线开头,剩下的部分可以是:字母、数字、下划线,最好遵循下述规范: 1.以字母开头 2.使用中划线或者下划线做单词的连接 3.同类型的用数字区分 4.对于文件名的命名最好在末尾加上拓展名 例如: sql_bak.tar.gz,log_bak.tar.bz2 5、不要带有空格、?、\*等特殊字符 6、不能使用bash中的关键字,例如**if,for,while,do** 等 7、不要和系统环境变量冲突 ### **变量值** 有三种**来源** 1、直接赋值 ip1="192.168.10.11" msg1=$(date) msg1=\`date\` 2、通位置参数获取命令行传入的变量值 $0 $1 $2 ... ${10}  (**运行命令后直接增加参数** ) 3、接收用户输入的变量值 **read -p** "请输入你的用户名\>\>\>: " **name** 格式化输出: echo "my name is $name,my age is $age" printf "my name is %s my age is %s\\n" $name $age ### **预定义变量-特殊符号** $\* 可以取到所有命令行传进来的位置参数(不包括$0) $@ 可以取到所有命令行传进来的位置参数(不包括$0) 区别: 应用该调用: ./2.sh 11 22 33 44 "55 66 77" 应该用:"$@" **可将55 66 77 识别为一个整体**   $# 获取命令行传进来的位置参数的个数(不包括$0) $$ 获取当前进程自己的pid号 $PPID 获取当前进程的父进程的id号 $? 获取上一条命令的运行成功与否的标志(0代表成功 非0代表失败) !$ 取上一条命令的参数部分 $! 取上一条命令的进程pid $\*案例:  ### **常量** 常量:不变的量 **readonly** x=111 y=2 readonly x ### 变量值的类型 为何要有类型 变量值是用来记录事物状态的,而事物的状态是分成多种多样的 例如:年龄、薪资、名字、性别 age=18 salary=3.1 name="egon" gender="male" gender="female" 针对不同种类的状态对应着就应该用不同类型的值去记录 补充: **强** 类型:数据类型的不可以被忽略,是有明确的边界---》不同类型之间**不能直接混用** **弱** 类型:数据类型的是可以被忽略,没有明确的边界---》不同类型之间有的**可以混用** 静态类型:**定义变量需要声明** 变量的类型,即在程序执行之前变量的类型就已经确定下来了 var age int = 18 动态类型: **定义变量不需要声明** 变量的类型,即需要在执行到具体代码的时候才能识别变量的类型 age=18 总结: shell是一门解释型、弱类型、动态语言 python是一门解释型、强类型、动态语言 go是一门编译型型、强类型、静态语言 ### 基本数据类型: 数字 整型 age=10 用于标识:年龄、等级、号码、个数 浮点型 salary=3.1 用于标识:薪资、身高、体重 字符串 定义:在引号内包含一串字符 单引号:**硬** 引用,会取消掉特殊符号的意义 双引号:**软** 引用,特殊符号会有意义 msg="hello world" 用于标识:描述性质的状态,名字、国籍、一段话、ip、url地址 **数组** 什么是数组: 数据就是一系列元素的集合 为何要用数组: 为了把多个值 / 元素汇总到一起,可以非常方便的去取第n个值 数组分成两种 普通数组: 用索引对应值,索引是编号反应的是位置,0代表第一个 -1倒数第一个 四种定义方式: array1=(111 3.3 "aaaa") array2=(\[0\]=111 \[2\]=3.3 \[1\]="aaaa") array4\[0\]=111 array4\[1\]=3.3 array4\[2\]="aaaaaaaa" array5=(\`ls\`) -----\> ls的结果之间存在空格,符合数组标准,可以直接输入 数组的取值 echo ${array1\[1\]} echo ${array1\[2\]} echo ${array5\[-1\]} echo ${array5\[-2\]} 记录一个人的爱好 hobbies=("read" "play" "music") echo ${hobbies\[1\]} **关联数组** :可以用字符串对应值 declare -A info info\["name"\]="egon" info\["age"\]=18 info\["gender"\]="male" echo ${info\["age"\]} ### 变量值操作 **获取变量长度** 已知变量msg='hello world!',请统计出变量中包含的字符数量 # 方法一: echo ${#msg} 12 # 方法二: echo $msg \| wc -L 12 # 方法三: echo $msg\|awk '{print length}' 12 # 方法四: expr length "$msg" #length是一个函数,注意因为msg的值有空格,所以$msg必须用引号包含 12 **切片----复制粘贴** msg="abc def" echo "${msg:3}" # 从3号索引开始,一直到最后,要带着引号,否则空格符号你看不到 def echo ${msg**:3:2**} # 从3号索引开始,**往后数**2个字符 d echo ${msg::3} # 从0开始,往后数3个字符 abc **截断** # =================》一、砍掉左边的字符《================= # 1.1 简单使用 url="www.sina.com.cn" echo ${url**#**www.} sina.com.cn # 1.2 结合\*=》非贪婪,默认情况下\*是非贪婪,尽可能地少"吃"字符 echo ${url**#\***w} # 碰到**第二个** 就停 ww.sina.com.cn # 1.3 结合\*=》贪婪,尽可能地多"吃"字符 echo ${url**##\***w} # \*会尽可能多地吃掉字符,一直匹配到**最远的**那个w才停下来 .sina.com.cn # =================》二、砍掉右边的字符《================= # 1.1 简单使用 url="www.sina.com.cn" echo ${url**%**.cn} www.sina.com # 1.2 结合\*=》非贪婪 \[root@egon \~\]# echo ${url%.\*} www.sina.com # 1.3 结合\*=》贪婪 \[root@egon \~\]# echo ${url%%.\*} www 内容替换 url="www.sina.com.cn" echo ${url/sina/baidu} # 变量名/被替换内容/替换内容 www.baidu.com.cn echo ${url**/**n/N} # 一个 www.siNa.com.cn echo ${url**//**n/N} # 贪婪 全部 www.siNa.com.cN **let** # (1) 变量的值 j=1 let ++j echo $j 2 # (2) 表达式的值 i=1 j=1 let x=i++ # 先把i赋值给x,然后再++ let y=++j # 先++j,然后再把j的结果赋值给y **作用域: (在什么地方会被看到)** 环境变量: 在当前shell及子shell生效(生效于整个环境) 全局变量: export 当前位置及其子子孙孙(其他终端中看不到) 自定义变量:仅在当前shell生效 set # 查看所有变量 env # 查看环境变量 shell接受指令方式不同-------交互式与非交互式shell 进入shell环境的方式--------登陆式与非登录式shell -----\> 都会加载 **/etc/bashrc** **登录shell /etc/profile----在此配置文件中加入** export 变量= .... ----\>在每次起bash时都会自动运行,所以改为了环境变量 bash ... 再起一个bash在其中运行命令 (非登陆式shell) source ... 在本bash运行命令 ## 三、引号   "" 软引用 特殊符号有自己的意义 '' 硬引用 不识别特殊符号的作用 -e \\n 换行 \\t 占位制表符  \`\` 取**命令的运行结果 (不用每次取值都进行查询)** 引号的嵌套  ## 四、元字符 ### 运算 bc # 支持浮点运算   保留两位,不四舍五入 expr(不支持浮点数)  $(()) $\[\] # echo $(((5-3)\*2)) # 不支持浮点运算 expr 判断是否为整数----特殊用法 ### 测试 test -d /etc/ ; echo $? # -s文件非空 -w可写 -r可读 -x可执行 -d目录 -f 普通文件 \[ -d /etc \] ; echo $? # \[\]左右内测要有空格 **$? 值为0---\>正确 值为1----\>错误** \[ "aaa" != "aaa" \];echo $? test "a" = "a" #字符串是否相同 (**==**也可以) # -z字符串长度为0 -n字符串长不为0 ####**关于字符串的测试,一定要给字符串加上引号**  \[ 10 -eq 10 \];echo $? # 测试数值 0 \[ 10 -eq 10 -a 10 \> 3 \];echo $? 0 # \[ $(id -u) -eq 0 \] \&\& echo "当前是超级用户" \|\| echo "you不是超级用户" 当前是超级用户 ####**-a -o 等同于 \&\& \|\|** ###  **浮点数比大小** # 需要注意的是:**bc的结果为1代表真,为0代表假** \[root@egon \~\]# echo "10.3 \>= 10.1" \| bc 1 \[root@egon \~\]# echo "10.3 != 10.1" \| bc 1 \[root@egon \~\]# echo "10.3 != 10.3" \| bc 0 ### 关系运算符 配合 (( )) ### **(())---\>可以做整数运算,也可以做判断(最好判断数字,字符串还是用test或则\[ \])**不支持浮点数 x=100 (($x\>10)) echo $? 0 (($x \< 10));echo $? 1 (($x == 100));echo $? 0 ### 赋值运算符 x=10 echo $x 10 x=10 ((x%3)) echo $x 10 ((x%=3)) echo $x 1 \*= /= %= 同上 ### 其他 $\[\] # 整数运算 $(()) # 整数运算 $() # 取命令结果  \[ \]与\[\[ \]\] 用法基本一致,**\[\[ \]\]支持正则匹配(内侧加空格)** \[\[ "$USER" == "root" \]\];echo $? # 注意内层\[\]中包含的内容必须左右两侧加空格 0 # 此外\[\[\]\]内部是可以使用正则的,注意:正则表达式不要加引号 num1=123 \[\[ "$num1" =\~ \^\[0-9\]+$ \]\];echo $? # 判断是否是数字 0 \[\[ "$num1" =\~ \^\[0-9\]+$ \]\] \&\& echo "是数字" 是数字 **\^\[0-9\]+$ 以数字开头 一直以数字循环 结尾为数字** **\^\[0-9\]+\[a-z\]$ 以数字开头 一直以数字循环 结尾为字母** **\^\[0-9\]+\[a-z\]+$ 以数字开头 先以数字循环在以字母循环 结尾为字母** !与 \^ -----\>取反  \[\] #不加空格 逐个取值 \[0-3\] 0 1 2 3 \[arvgba\] a r v g b \[root@localhost \~\]# touch a1c a2c axc aXc axd \[root@localhost \~\]# ls a?c a1c a2c axc aXc \[root@localhost \~\]# ls a\[1x\]c a1c axc \[root@localhost \~\]# ls a\[a-z\]c axc aXc \[root@localhost \~\]# ls a\[A-Z\]c # 不区分大小写 axc aXc \[root@localhost \~\]# ls a\[x\]c axc \[root@localhost \~\]# ls a\[X\]c aXc \[root@localhost \~\]# ls a\[0-9\]c a1c a2c \[root@localhost \~\]# ls /dev/sd\[a-z\]\* /dev/sda /dev/sda1 /dev/sda2 /dev/sda3 /dev/sdb1 @分隔符 $ 取变量值 \& # 在最后 后台运行 # 在\&\>..... 表示同时写入正确输出和错误输出(文件描述符) () #在子shell 中执行 当前shell不显示  = 赋值 == 判断相等性 \\ 转义特殊字符  ; \&\& \|\| \[root@localhost home\]# gagaga**;**ls # 不论前一条命令运行成功与否,都会执行后续命令 bash: gagaga: 未找到命令... egon \[root@localhost home\]# gagaga **\&\&** ls # 只有前一条命令执行成功,才会执行后续命令 bash: gagaga: 未找到命令... \[root@localhost home\]# ls /test **\|\|** mkdir /test # 前一条命令执行不成功才会执行后续命令 0.txt 1.txt 2.txt 3.txt 4.txt 5.txt 6.txt 7.txt 8.txt 9.txt ? 任意一个字符 \* 任意多个字符 **{ }**执行一组命令 **;**不论成功与否继续执行  # \`\`与$() \` \` 命令替换 等价于 $() 反引号中的shell命令会被先执行 \[root@localhost \~\]# touch \`date +%F\`_file1.txt \[root@localhost \~\]# touch $(date +%F)_file2.txt \[root@localhost \~\]# disk_free3="df -Ph \|grep '/$' \|awk '{print $4}'" # 错误 \[root@localhost \~\]# disk_free4=$(df -Ph \|grep '/$' \|awk '{print $4}') # 正确 \[root@localhost \~\]# disk_free5=\`df -Ph \|grep '/$' \|awk '{print $4}'\` # 正确 ## 流程控制 ### if if 条件;then 要执行的命令1 要执行的命令2 ... elif 条件;then 要执行的命令1 要执行的命令2 ... elif 条件;then 要执行的命令1 要执行的命令2 ... ... else 要执行的命令1 要执行的命令2 ... fi  read -p "请输入用户名" name read -p "请输入密码" password if \[ "$name" = "egon" \] \&\& \[ "$password" = "123" \];then echo "用户登录成功" else echo "用户登录失败" fi  ### **for循环** #===========》Shell风格语法 for 变量名 \[ in 取值列表
do
循环体
done
#===========》C语言风格语法
for ((初值;条件;步长))
do
循环体
done
循环次数
for i in `seq 1 3`; # for i in {1..3}
do
echo $i
done
for i in {1..3};
do
echo $i
done
案例
for i in `ls /test`;
do
echo i \|grep "txt" &>/dev/null
if [ $? -eq 0 ];then
#echo "$i 是txt结尾"
mv /test/i /test/{i}_bak
else
echo "$i 不是txt结尾"
fi
for i in {1..255}
do
(ip_addr="192.168.71.$i"
ping -c1 $ip_addr &>/dev/null
if [ $? -eq 0 ];then
echo "$ip_addr -------------- ok" >> /tmp/ip.log
else
echo "$ip_addr -------------- no" >> /tmp/ip.log
fi) &
done #写成一条命令放入后台提高运行速度
while循环
一、while语句结构:条件为真时,执行循环体代码
while 条件
do
循环体
done
二、until语法结构:条件为假时,一直执行循环体代码,直到条件变为真
until 条件
do
循环体
done
continue:默认退出本次循环
break:默认退出本层循环
案例:监控web页面状态信息,失败三次进行报警
cat f.sh
#!/bin/bash
timeout=3
fails=0
url=$1
while true
do
wget --timeout=timeout --tries=1 url -q
curl --connect-timeout timeout url &>/dev/null
if [ $? -ne 0 ]
then
let fails++
echo "错误次数=====>$fails"
else
echo "页面访问成功"
break
fi
if [ $fails -eq 3 ]
then
echo "失败3次,超过最大次数"
break
fi
done
测试:
./f.sh https://www.egon.com
错误次数=====>1
错误次数=====>2
错误次数=====>3
失败3次,超过最大次数
case (不常用 = if)
case 变量 in
模式1)
命令序列1
;;
模式2)
命令序列2
;;
模式3)
命令序列3
;;
*)
无匹配后命令序列
esac
#!/bin/bash
read -p "username: " -t 5 username
echo
if [ -z $username ];then
username="default"
fi
case $username in
root)
echo "管理员用户"
;;
user)
echo "普通用户"
;;
default)
echo "默认用户"
;;
*)
echo "其他用户"
esac