Shell基础

一、前言

什么是编程

编程就是人把自己想让计算机做的事情,用计算机

能听懂的语言(编程语言)给翻译下来

所以编程分为两个环节:

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} ![](https://i-blog.csdnimg.cn/direct/2656550c00b04719a230914c026057c0.png) ![](https://i-blog.csdnimg.cn/direct/fc5adf4a6e0e415e811af74a6e0e8896.png)(**运行命令后直接增加参数** ) 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 识别为一个整体** ![](https://i-blog.csdnimg.cn/direct/270a9b9a9c80497baee1ca469c3642c3.png) ![](https://i-blog.csdnimg.cn/direct/305710c45db8482d9af7557e20265bfd.png) $# 获取命令行传进来的位置参数的个数(不包括$0) $$ 获取当前进程自己的pid号 $PPID 获取当前进程的父进程的id号 $? 获取上一条命令的运行成功与否的标志(0代表成功 非0代表失败) !$ 取上一条命令的参数部分 $! 取上一条命令的进程pid $\*案例: ![](https://i-blog.csdnimg.cn/direct/bdc5f49c02944865afd517b6177e2264.png) ### **常量** 常量:不变的量 **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运行命令 ## 三、引号 ![](https://i-blog.csdnimg.cn/direct/c5819e3d023041dfa34ee85517620f98.png) ![](https://i-blog.csdnimg.cn/direct/e912606eee2640a6bd1a744de82e5e2f.png) "" 软引用 特殊符号有自己的意义 '' 硬引用 不识别特殊符号的作用 -e \\n 换行 \\t 占位制表符 ![](https://i-blog.csdnimg.cn/direct/485fb8d228e34a88b1fa95290fbe0cbe.png) \`\` 取**命令的运行结果 (不用每次取值都进行查询)** 引号的嵌套 ![](https://i-blog.csdnimg.cn/direct/73df9d364b4d40bd93d89c6eee1762fc.png) ## 四、元字符 ### 运算 bc # 支持浮点运算 ![](https://i-blog.csdnimg.cn/direct/ac066ae9be644a3cb1e3cd17f545ca6c.png) ![](https://i-blog.csdnimg.cn/direct/82f4b14726ba45388ff8a9bbb69aab4b.png) 保留两位,不四舍五入 expr(不支持浮点数) ![](https://i-blog.csdnimg.cn/direct/1b1d80ed9f2c4b09a8203428042e0032.png) $(()) $\[\] # 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 ####**关于字符串的测试,一定要给字符串加上引号** ![](https://i-blog.csdnimg.cn/direct/b6346121e0b64e28960ace2317c32ed7.png) \[ 10 -eq 10 \];echo $? # 测试数值 0 \[ 10 -eq 10 -a 10 \> 3 \];echo $? 0 # \[ $(id -u) -eq 0 \] \&\& echo "当前是超级用户" \|\| echo "you不是超级用户" 当前是超级用户 ####**-a -o 等同于 \&\& \|\|** ### ![](https://i-blog.csdnimg.cn/direct/7769c1267d5240aabad7dee39065e2ce.png) **浮点数比大小** # 需要注意的是:**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 \*= /= %= 同上 ### 其他 $\[\] # 整数运算 $(()) # 整数运算 $() # 取命令结果 ![](https://i-blog.csdnimg.cn/direct/b764acb3a9524fad895444dda863d407.png) \[ \]与\[\[ \]\] 用法基本一致,**\[\[ \]\]支持正则匹配(内侧加空格)** \[\[ "$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\]+$ 以数字开头 先以数字循环在以字母循环 结尾为字母** !与 \^ -----\>取反 ![](https://i-blog.csdnimg.cn/direct/a359a115c4f0425eb12641b82fcbbcb7.png) \[\] #不加空格 逐个取值 \[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不显示 ![](https://i-blog.csdnimg.cn/direct/ad9acafe6b114029937e65f4fd771df1.png) = 赋值 == 判断相等性 \\ 转义特殊字符 ![](https://i-blog.csdnimg.cn/direct/bb38fad2182346ea964b5ab67eccae77.png) ; \&\& \|\| \[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 ? 任意一个字符 \* 任意多个字符 **{ }**执行一组命令 **;**不论成功与否继续执行 ![](https://i-blog.csdnimg.cn/direct/dd3a8d22eded4abfa5ad6d08f58ae75a.png) # \`\`与$() \` \` 命令替换 等价于 $() 反引号中的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 ![](https://i-blog.csdnimg.cn/direct/bfd6171613ea4a0db20d33abc752b866.png) read -p "请输入用户名" name read -p "请输入密码" password if \[ "$name" = "egon" \] \&\& \[ "$password" = "123" \];then echo "用户登录成功" else echo "用户登录失败" fi ![](https://i-blog.csdnimg.cn/direct/e4ac2dbc899341c9babfe432c940be09.png) ### **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

相关推荐
奔跑的废柴3 分钟前
Jenkins学习(B站教程)
运维·学习·jenkins
Tee xm4 分钟前
清晰易懂的 Jenkins 安装与核心使用教程
linux·windows·macos·ci/cd·jenkins
曹瑞曹瑞11 分钟前
Linux制作deb安装包
linux·运维
YZF_Kevin14 分钟前
centos安装dashboard详细步骤
linux·运维·centos
kobe_OKOK_25 分钟前
CentOS 部署 Nodejs
linux·运维·centos
如若12325 分钟前
《在 Ubuntu 22.04 上安装 CUDA 11.8 和 Anaconda,并配置环境变量》
linux·运维·ubuntu
源代码•宸1 小时前
Visual Studio Code SSH 连接超时对策( keep SSH alive)
运维·服务器·ide·经验分享·vscode·ssh
zyx没烦恼1 小时前
Linux 下 日志系统搭建全攻略
linux·服务器·开发语言·c++
Tee xm2 小时前
清晰易懂的 Flutter 开发环境搭建教程
linux·windows·flutter·macos·安装
不摆烂选手2 小时前
Ubuntu之Makefile入门
linux·ubuntu·makefile·正点原子imx6ull学习笔记