Shell编程

1.什么是shell?

shell是一种应用程序,它提供了用户和内核进行操作的接口。
image.png
通俗的说shell就是一种让你和操作系统能进行对话的一种应用程序。用户登陆Linux系统后,shell就会调如到内存中执行,它可以把用户输入的命令转化为计算机可以理解的命令给内核执行。
Linux中不止有一种shell,通过cat /etc/shells 查看当前Linux中的shell:
image.png
sh->bash(链接到bash) ,bash是许多Linux平台内定的shell,bash是一个命令处理器,运行在窗口,能让用户直接通过键盘输入命令,bash还能从脚本中读取linux命令,称之为脚本,bash支持通配符、管道、命令替换、条件判断等逻辑控制语句。

1.1 Shell脚本

shell是一种脚本语言,采用解释执行,shell脚本就是一个文件,里面就是程序的代码,计算机执行这个文件,就是读取其中的命令进行执行;执行shell的时候是以非交互式的方式进行,一旦代码有问题,就出现非交互,表示没有一次执行完所有的命令,需要重新修改;
shell脚本语言非常适合处理纯文本类型数据,如日志、配置文件、文本、网页文件。

1.2 什么是shebang?为什么shell脚本第一行要#!开头?

shebang是指出现在文本文件的第一行前两个字符 #!;在UNIX系统中,程序会分析#!字符后面的内容,作为解释器的指令,以#!/bin/sh 开头的文件,程序在执行的时候会调用/bin/sh,也就是bash解释器,以#!/usr/bin/python 开头的文件,就代表指定python解释器去执行。
如果没有指定解释器,就会使用默认的解释器;
如果指定的解释器没有可执行的权限,则会报:bad interpreter:permission denied.
如果脚本指定的解释器不存,则会报:bad interpreter:no such file or directory.
注意:解释器的名字要写绝对路径,不然会在当前的path去找

1.3 执行脚本

编写一个最简单的shell脚本,文件为test.sh

#!/bin/bash
echo "hello,world"

执行shell脚本的方式:

  • 1.bash 文件名.sh
  • 2.相对路径或者绝对路径+文件名.sh ,需要有可执行的文件权限,没有执行权限要加入权限

这两种方式都是在子shell中执行,子shell就是在原来的shell里面嵌套一个shell 执行,子shell执行完毕之后的结果不会给父shell造成影响;
source 文件.sh 或者 . 文件.sh是在本shell执行(不需要执行权限)
image.png
使用pstree查看进程树(树的形式查看进程之间的派生关系),可以看到:我们运行的pstree是在sshd->sshd->bash->pstree上,第一个sshd是远程服务器运行了一个sshd服务,然后我使用xshell连接,又在原来的sshd基础上派生出一个子进程sshd,然后就会自动开启一个bash进程,因为linux是通过shell管理的,所以要生成一个bash进程,然后就是我们执行的命令pstree;

1.4 切换shell

image.png
这样就进入到了子shell的bash里,然后使用exit退出,再进入到sh中:
image.png

1.5 内核:Linux心脏

Linux 内核并不是操作系统,它是一个完整系统的组成部分。Linux 内核控制着Linux 操作系统的基本硬件,具有很多功能,如文件管理、内存、多线程、网络 等等。
image.png

2.变量

shell中的变量大致可分为如下几类:

  • 系统环境变量
  • 自定义环境变量
  • 特俗符号变量

2.1 自定义变量

定义的规则:
a.变量的值有空格,因为linux中的空格后面都是要加参数的,如果有空格必须要用双引号
b.变量不能以下划线,关键字和数字开头
c.bash环境中,默认类型都是字符串,无法直接进行数值计算
d.等号两边不能有空格
变量的类型:
自定义局部变量:定义在一个脚本文件中的变量,只能在这个脚本文件中使用
自定义常量
自定义全局变量
image.png
单引号、双引号、反引号、无引号:

  • a.单引号被识别为普通字符串,就是不识别引号里面的特殊字符,属于强引用
  • b.双引号是双引号里面的特殊字符是可以被识别的,是弱引用。
  • c.反引号中的内容一般是一行或多行命令,当这些命令被反引号引着,命令执行结果就会以字符串的形式被保留下来。$(ls /etc) 也可以将命令执行的内容返回;
  • d.无引号:如果是连续的符号可以不加引号,但是如果有空格就有歧义,最好使用双引号。

image.png
局部变量和全局变量

a.局部变量的子shell不能访问,只在当前的shell里可用,全局变量子shell可用;
系统全局变量 : $USER  $HOME  $SHELLset 当前系统和用户定义的全局变量和局部变量
env 查看系统定义的全局变量

1.局部变量 变量名=值 不能有空格 输出 $变量名 值的类型可以不用管;
2.export  变量名  导出成全局变量 在子shell 也行
	:如果在外面定义全局变量,在子shell改变退回外部shell之后不会改变,子shell 更改 父shell不改变。
3.a=1+5 不等于6 默认都是字符串
	:a=$((1+5))=$[1+5]
	:定义只读变量 readonly b=5;
	:unset 变量名 撤销变量 只读变量不能unset

2.2特殊变量:

:$0 表示的是脚本名称,要是输入相对路径,输出的就是相对,绝对的话输出的就是绝对路径;
:$1-9 表示的是1-9的参数 10以上用${12} 包裹住
:$# 获取所有输入参数个数
:$* 获取命令行中的所有参数,把所有的参数的个数看成一个整体
:$@ 获取命令行中的所有参数,把每个参数区分对待
:$? 最后一次执行命令的返回状态,数值0表示正确执行 非0证明上一个命令执行不正确。
:$$ 获取当前shell脚本的进程号。我们在脚本开发中,根据这个变量的返回值,我们可以开发一些启停脚本等进程管理的文件。
:$! 取出上一次、后台进程的PID。
:$_ 取得上一条命令传入的最后一个参数。

$@ 和 $* 的区别:
$@ 和 ∗ 都是获得命令行中的所有参数,并当作整体看待,当不被双引号包裹的时候,两者没有任何区别,当被双引号包裹的时候, " * 都是获得命令行中的所有参数,并当作整体看待,当不被双引号包裹的时候,两者没有任何区别,当被双引号包裹的时候," 都是获得命令行中的所有参数,并当作整体看待,当不被双引号包裹的时候,两者没有任何区别,当被双引号包裹的时候,"*" 会将所有的参数从整体上看作一份数据,而不是把每个参数都看作一份数据。也就是参数之间是无法分隔的,因为是一份数据嘛。而"$@" 依然是将每个参数都看作一份数据,彼此之间是独立的,就是有参数个数份数据。

#!/bin/bash
echo "输出$0,$1,$2" $0 $1 $2
echo "输出$#" $#
echo "输出$*" $*
echo "输出$@" $@
for i in "$*"
do 
	echo "$i"
done
echo "=================="
for i in "$@"
do 
	echo "$i"
done

输出为:
image.png
将echo中的双引号改为单引号,双引号包裹会把变量解析,单引号是强引用,不会解析,输出为:
image.png

2.3 环境变量

参考链接
环境变量可以由系统、用户、Shell 以及其他程序来设定,环境变量是一个可以被赋值的字符串,赋值范围包括数字、文本、文件名、设备以及其他类型的数据。Linux中环境变量的名称一般都是大写的,这是一种约定俗称的规范。
使用
echo
查看单个环境变量,env查看当前系统定义的所有环境变量,使用set查看所有本地定义的环境变量。使用 unset 删除指定的环境变量,set 也可以设置某个环境变量的值。清除环境变量的值用 unset 命令。
image.png
程序的执行和操作系统的运行都需要环境,这些环境就是由环境变量组成的;

2.3.1 环境变量的分类:
  1. 按生效的范围分类
    1. 系统环境变量:公共的,对所有的用户生效
    2. 用户环境变量:私有的,自定义的,只对该用户生效
  2. 生存周期分类
    1. 永久环境变量:在环境变量脚本文件中配置,用户每次登录时会自动执行这些脚本,相当于永久生效。
    2. 临时环境变量:使用时在Shell中临时定义,退出Shell后失效,使用export导出的环境变量。

Linux的环境变量:Linux环境变量也称之为Shell环境量变,以下划线和字母打头,由下划线、字母(区分大小写)和数字组成,习惯上使用大写字母,例如PATH、HOSTNAME、LANG等。
image.png

2.3.2 Linux中常用的环境变量
1PATH 可执行程序的搜索目录
可执行程序的搜索目录,可执行程序包括Linux系统命令和用户的应用程序。
如果可执行程序的目录不在PATH指定的目录中,执行时需要指定目录。
PATH环境变量存放的是目录列表,目录之间用冒号:分隔,最后的圆点.表示当前目录。
export PATH=目录1:目录2:目录3:......目录n:.
2LANG Linux系统的语言、地区、字符集,LANG变量的具体用法本文后面的章节中有详细的介绍。
3HOSTNAME 主机名
4SHELL 当前使用的shell解释器
5HISTSIZE 保存的历史命令数
6USER 当前登陆的用户名
7HOME 当前登陆的用户的主目录
8)LD_LIBRARY_PATH C/C++语言动态链接库文件搜索的目录,它不是Linux缺省的环境变量,但对C/C++程序员来说非常重要,
9)CLASSPATH JAVA语言库文件搜索的目录,它也不是Linux缺省的环境变量,但对JAVA程序员来说非常重要

PATH的作用:
image.png
在用户的.bash_profile文件中,会对PATH进行扩充,将用户的应用程序也写进去,如下:
export PATH= P A T H : PATH: PATH:HOME/bin

2.3.3 设置环境变量
1. 变量名=值 或者 export 变量名=值
例如:export ORACLE_HOME=/oracle/home 采用export设置的环境变量,在退出Shell后就会失效,下次登录时需要重新设置。
如果希望环境变量永久生效,需要在登录脚本文件中配置。(每次执行的时候都会运行脚本文件)

2.系统环境变量
系统环境变量对全部的用户生效,设置系统环境变量有三种方法。
a)在/etc/profile文件中设置
用户登录时执行/etc/profile文件中设置系统的环境变量。但是,Linux不建议在/etc/profile文件中设置系统环境变量。
b)在/etc/profile.d目录中增加环境变量脚本文件,这是Linux推荐的方法。
/etc/profile在每次启动时会执行 /etc/profile.d下全部的脚本文件。
/etc/profile.d比/etc/profile好维护,不想要什么变量直接删除 /etc/profile.d下对应的 shell 脚本即可。
c)在/etc/bashrc文件中设置环境变量
该文件配置的环境变量将会影响全部用户使用的bash shell。但是,Linux也不建议在/etc/bashrc文件中设置系统环境变量。
因此设置系统环境变量的时候建议使用b中的方法;

3.用户环境变量
用户环境变量只对当前用户生效,设置用户环境变量也有多种方法。
在用户的主目录,有几个特别的文件,用ls是看不见的,用ls .bash_*可以看见。
a).bash_profile(推荐首选)
当用户登录时执行,每个用户都可以使用该文件来配置专属于自己的环境变量。
b).bashrc
当用户登录时以及每次打开新的Shell时该文件都将被读取,不推荐在里面配置用户专用的环境变量,
因为每开一个Shell,该文件都会被读取一次,效率肯定受影响。
c).bash_logout
当每次退出系统(退出bash shell)时执行该文件。
d).bash_history
保存了当前用户使用过的历史命令。

在脚本文件中设置的环境变量不会立即生效,退出Shell后重新登录时才生效,或者用source命令让它立即生效

image.png
image.png
环境变量脚本的执行顺序:
/etc/profile->/etc/profile.d->/etc/bashrc->用户的.bash_profile->用户的.bashrc
同名的环境变量,如果在多个脚本中有配置,以最后执行的脚本中的配置为准。

3.运算符

Shell支持多种运算符:

  • 算术运算符
  • 关系运算符
  • 布尔运算符
  • 逻辑运算符
  • 字符串运算符
  • 文件测试运算符

bash不支持简单的数学运算,可以通过awk和expr来实现,expr最为常见。
expr表达式和运算符之间要有空格,完整的表达是要被反引号包围;第二种方式也可以实现运算;

1.expr 1 + 2  结果是3
2. $[运算式] 或者 $((运算式))
3. a = $(expr 1 + 2) 或者 `expr 1 + 2` 都叫做命令替换

image.png

#!/bin/bash
echo $[ $1 + $2 ] #条件表达式放在方括号之间,要有空格
如果不加,报错:line *: [: missing `]'

3.1 算术运算符

#!/bin/bash
a=100
b=20
val=`expr $1 + $2`
echo "a+b:$val"

val=`expr $1 - $2`
echo "a-b:$val"

val=`expr $1 \* $2` #乘法这里要有个\
echo "a*b:$val"

val=`expr $1 / $2`
echo "a/b:$val"

val=`expr $1 % $2`
echo "a%b:$val"

if [ $a == $b ];then
echo "a=b"
fi
if [ $a != $b ];then
echo "a!=b"
fi

image.png

3.2 关系运算符

image.png

#!/bin/bash
a=10
b=20
if [ $a -eq $b ]
then
   echo "$a -eq $b :a equal b"
else
   echo "$a -eq $b :a  not equal b"
fi

if [ $a -ne $b ]
then
   echo "$a -ne $b :a not  equal b"
else
   echo "$a -ne $b :a equal b"
fi


if [ $a -gt $b ]
then
   echo "$a -gt $b :a great than b"
else
   echo "$a -gt $b :a  less equal than b"
fi

if [ $a -ge $b ]
then
   echo "$a -ge $b :a great  equal b"
else
   echo "$a -ge $b :a  less  b"
fi

3.3 布尔运算符

image.png

#!/bin/bash
a=10
b=20
if [ $a -lt 100 -a $b -gt 15 ];then
    echo "$a less than 100 and $b greater than 15"
fi
if [ $a -lt 10 -o $b -gt 15 ];then
    echo "$a less than 10 or $b greater than 15"
fi

3.4 逻辑运算符

image.png

#!/bin/bash
a=10
b=20
if [[ $a -lt 100 && $b -gt 100 ]];then
   echo "true"
else
   echo "false"
fi

if [[ $a -lt 100 || $b -gt 100 ]];then
   echo "true"
else
   echo "false"
fi

3.5 字符串运算符

image.png

#!/bin/bash
a="abc"
b="efg"

if [ $a = $b ]
then
    echo "a=b"
else
    echo "a!=b"
fi

if [ -z $a ]
then
   echo "-z $a : 字符串长度为 0"
else
   echo "-z $a : 字符串长度不为 0"
fi

if [ $a ]
then
   echo "$a : 字符串不为空"
else
   echo "$a : 字符串为空"
fi

3.6 文件测试运算符

image.png

#!/bin/bash

file="/root/ShellHome/5.sh"

if [ -r $file ];then
    echo "文件可读"
else
    echo "文件不可读"
fi

if [ -w $file ];then
    echo "文件可写"
else
    echo "文件不可写"
fi

if [ -x $file ];then
    echo "文件可执行"
else
    echo "文件不可执行"
fi

if [ -f $file ]
then
   echo "文件为普通文件"
else
   echo "文件为特殊文件"
fi
if [ -d $file ]
then
   echo "文件是个目录"
else
   echo "文件不是个目录"
fi
if [ -s $file ]
then
   echo "文件不为空"
else
   echo "文件为空"
fi
if [ -e $file ]
then
   echo "文件存在"
else
   echo "文件不存在"
fi

if [ -u $file ]
then
   echo "文件设置了suid"
else
   echo "文件未设置了suid"
fi

4.流程控制

4.1 条件判断

注意:if else 的 […] 判断语句中大于使用 -gt,小于使用 -lt
如果使用 ((…)) 作为判断语句,大于和小于可以直接使用 ><

1.if [];then 
fi
2.if []&&[] 双重条件 if [ 条件1  -a  条件2  ]   if [  -o   ]
3.写成一行的情况如下:
if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi

4.2 循环

for循环

1.for((初始值;循环条件;变量变化))
do
done
2.for i in 1 2...
3.写成一行:
for var in item1 item2 ... itemN; do command1; command2… done;

while循环

while condition
do
    command
done

4.3 case

3. case $变量名 in
值1)
	程序1
;;
值2)
	程序2
;;
...
esac 表示结束

4.4 使用案例

 for i in {1..100};do sum=$[$sum+$i];done;echo $sum
#!/bin/bash
for((i=0;i <= $1;i++))
do 
    sum=$[ $sum + $i ];
done
echo $sum;
a=1;
while [ $a -le $1 ]
do
    sum2=$[ $sum2 + $a ];
    a=$[$a + 1]
    #let sum2+=a
    #let a++
done
echo $sum2

5.读取控制台输入

1) read (选项) (参数) 
	选项:-p  指定读取时的提示符
		   -t  指定读取时等待时间 不加-t一直等待
    参数:
      变量:指定读取值得变量名
#!/bin/bash
read -t 10 -p "请在10s内输入你的狗名" name
echo $name

6.函数

linux shell中用户可以自定义函数,然后在shell脚本中调用,格式如下所示

[function] funname [()]{
    函数体
    [return int;]
}

简化写法:

函数名(){
    函数体
}

标准格式:

function 函数名(){
	命令序列
}

return int 表示函数的返回值,return的值只能在0-255之间;

6.1 变量作用域

局部变量:作用域是函数的生命周期;在函数结束时被自动销毁。定义局部变量的方法:

local VAR=VALUE

本地变量:作用域是运行脚本的shell进程的生命周期;因此,其作用范围为当前shell。

#!/bin/bash

var="hello,world"
function show(){
    local var="hi,var is changed!"
    echo $var
}
echo $var
show
echo $var

6.2 函数调用

定义函数的代码段不会自动执行,而是在调用时执行;在函数定义好后,用户可以在shell 中直接调用,调用时不用带上();调用 Shell 函数时可以给它传递参数,也可以不传递。如果不传递参数,直接给出函数名字即可。
因为shell是解释性语言,所以需要先定义,才能调用;

#!/bin/bash

function show(){
    echo "第一个参数为 $1 !"
    echo "第二个参数为 $2 !"
    echo "第十个参数为 $10 !"
    echo "第十个参数为 ${10} !"
    echo "第十一个参数为 ${11} !"
    echo "参数总数有 $# 个!"
    echo "作为一个字符串输出所有参数 $* !"
}
show 0 1 2 3 4 5 6 7 8 9 10 11

7.系统函数

1.basename 文件路径[suffix] 做文件剪辑
2.dirname 最后一个/之前的剪切下来 文件所在的目录

可以有内置的函数,也可以下载其他的命令,然后放在bin目录下就可以用;

8.综合应用案例

每天对指定目录归档备份的脚本,输入一个目录名称(末尾不带/),将目录下所有文件按天归档保存,并将归档日期附在归档文件上,放在/root/archive 下

1.sh  2.sh  3.sh  4.sh  5.sh  6.sh  7.sh  8.sh  hello.sh
[root@iZ2zeflmaf303pxr6lpexvZ scripts]# vim 8.sh

        echo "目录存在"
else
        echo "目录不存在"
        exit
fi

DIR_NAME=$(basename $1)
DIR_PATH=$(cd $(dirname $1);pwd)

DATE=$(date +%y%m%d)

FILE=archive_${DIR_NAME}_$DATE.tar.gz
DEST=/root/archive/$FILE

echo "开始归档"

tar -czf $DEST $DIR_PATH/$DIR_NAME

if [ $? -eq 0 ]
then
        echo "归档成功"
        echo "归档文件为" $DEST
fi

8.1 正则表达式

1.^ 匹配开头 ^a以a开头的
2.$ 匹配一行的结束
3.cat 文件 | grep -n ^$ 看一下有多少空行
4. . 匹配任意的字符
5. * 不单独使用,前面一个字符出现任意多次
6. [] 匹配中括号里面的内容一次
	[]* 任意字符串
7.搜索 $符号 '\$' 要进行转义
8. -E 拓展的字符表达式

8.2 文本处理工具

1.cut [选项参数] filename
	:-f 列好,提取第几列
	:-d 分隔符,指定分隔符,默认是\t
	:-c 按字符进行切割
2.cat /etc/passwd | grep bash$ | cut -d ":" -f 1,2-7
3.ifconfig ens33 | grep network | cut -d " " -f 10
awk [选项参数] '/pattern1/{action1}         /pattern1/{action1}' filename
pattern:文本匹配模式
action1:找到匹配的文本所执行的命令
-F 指定输入文件分隔符 
-V 赋值一个用户定义变量

相关推荐

  1. Shell编程

    2024-05-11 21:44:06       59 阅读
  2. shell编程

    2024-05-11 21:44:06       26 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-05-11 21:44:06       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-05-11 21:44:06       100 阅读
  3. 在Django里面运行非项目文件

    2024-05-11 21:44:06       82 阅读
  4. Python语言-面向对象

    2024-05-11 21:44:06       91 阅读

热门阅读

  1. leetcode刷题:三数之和

    2024-05-11 21:44:06       33 阅读
  2. 思科基础命令(对标华为的HCIA知识)

    2024-05-11 21:44:06       34 阅读
  3. Goland GC

    Goland GC

    2024-05-11 21:44:06      35 阅读
  4. pg_basebackup备份恢复实战

    2024-05-11 21:44:06       26 阅读
  5. LeetCode 151.翻转字符串里的单词

    2024-05-11 21:44:06       32 阅读
  6. 【Linux】使用Valgrind定位内存增长问题

    2024-05-11 21:44:06       28 阅读
  7. C语言中的静态库和动态库的制作和使用

    2024-05-11 21:44:06       25 阅读
  8. 为何循环产生 SIGSGEV 信号处理函数打印

    2024-05-11 21:44:06       29 阅读
  9. Python 多进程和多线程在加速程序运行上的差别

    2024-05-11 21:44:06       34 阅读
  10. es 7.1.0 启用身份认证

    2024-05-11 21:44:06       26 阅读
  11. 送外卖面试回顾

    2024-05-11 21:44:06       26 阅读
  12. spring 创建bean的过程

    2024-05-11 21:44:06       31 阅读