shell编程

一、shell简介

1、语言类型

编译型:

(1)只须编译一次就可以把源代码编译成机器语言,后面的执行无须重新编译,直接使用之前的编译结果就可以;因此其执行的效率比较高;

(2)编译性语言代表:C、C++、Pascal/Object Pascal(Delphi);

(3)程序执行效率比较高,但比较依赖编译器,因此跨平台性差一些;

解释型:

(1)源代码不能直接翻译成机器语言,而是先翻译成中间代码,再由解释器对中间代码进行解释运行;

(2)程序不需要编译,程序在运行时才翻译成机器语言,每执行一次都要翻译一次;

(3)解释性语言代表:Python、JavaScript、Shell、Ruby、MATLAB等;

(4)运行效率一般相对比较低,依赖解释器,跨平台性好;

2、shell定义

1、shell是一种命令解释器,用于连接用户与操作系统内核。

2、shell接收用户输入的命令然后调用内核接口(函数),内核按照shell指令去执行操作,并把结果交给shell,最后由shell将结果呈现给用户。

3、在shell中输入的命令有一部是自带的,叫内置命令。一部分是其他应用程序,叫外部命令。

4、shell命令可以无限拓展(安装软件)

5、shell可以让多个外部程序发生连接

6、shell 也是一种程序设计语言,他有变量,关键字,各种控制语句,有自己的语法结构,利用shell程序设计语言可以编写功能很强、代码简短的程序。

3、shell的分类

# cat /etc/shells 查看系统支持那些shell

/bin/sh

/bin/bash

/sbin/nologin

/usr/bin/sh

/usr/bin/bash

/usr/sbin/nologin

/bin/tcsh

/bin/csh

#chsh –l 查看所有的shell

4、linux命令的分类 

内嵌命令 外部命令 别名 函数 


别名: alias

查询别名: # alias

设置别名: # alias 命令名称='动作'

取消别名: # unalias 别名名称 


历史命令

history, 默认只记录1000条

清除历史命令: history -c

如要每行历史命令前面加上时间

# echo 'export HISTTIMEFORMAT="%F %T "' >> ~/.bashrc

# source ~/.bashrc

多条命令执行用 ; 隔开 :date;whoami

二、shell的特性及特点

1、外部命令与内置命令

内置命令在系统启动时就调入内存,是常驻内存的,所以执行效率高。

而外部命令是系统的软件功能,用户需要时才从硬盘中读入内存

使用type查看是否是内外部

[root@192 ~]# type wget
wget is /usr/bin/wget
[root@192 ~]# type cd
cd is a shell builtin

echo $PATH查看外部命令存储路径


 外部:

外部命令也称为文件系统命令,是bash shell之外的程序,它并不是shell 的一部分。外部命令一般位于/bin、/usr/bin、/sbin或/usr/sbin中。

可以使用which查看命令路径加以辨别

当执行外部命令时,Linux系统会创建出一个子进程(这种操作被称为衍生)。

可以使用ps -f 查看

内部:

内置命令和shell是一体的,它们作为shell工具的组成部分存在,不需要通过衍生出子进程来执行,也不需要打开程序文件。

所以内置命令的执行速度要更快,效率也更高。

2、GNU/bash shell特点 

1)命令和文件自动补齐

2)命令历史记忆功能 上下键、!number(命令的序号)、!

3)快捷键

Ctrl+a 切换到命令行开始(跟home一样,但是home在某些unix环境下无法使用)

Ctrl+e 切换到命令行末尾

Ctrl+u 清除剪切光标之前的内容

Ctrl+k 清除剪切光标之后的内容

ctrl+y 粘贴刚才所删除的字符

ctrl+左右键 快速移动光标

Ctrl+r 在历史命令中查找,输入关键字调出之前的命令

4)前后台作业控制 &、nohup,^C、^Z(停止),bg (将一个在后台暂停的命令,变成继续执行)、fg (将后台中的命令调至前台继续运行)

5)输入输出重定向 0,1,2 > >> 2> 2>> 2>&1 &> cat < /etc/hosts cat <<EOF cat >file1 <<EOF

6)管道 | tee

7)命令排序

&& || ; 具备逻辑判断,连接命令用的

; 无论前面是否执行成功,分号后的命令都会继续执行

&&:前面执行成功,后面的才继续执行

||:前面命令不成功,后面的命令才会继续

8)shell通配符(元字符)表示的不是本意

* 匹配任意多个字符 ls in* rm -rf * rm -rf *.pdf find / -iname "*-eth0" yum -y install epel*

? 匹配任意一个字符 touch love loove live l7ve; ll l?ve

[] 匹配括号中任意一个字符 [abc] [a-z] [0-9] [a-zA-Z0-9]

() 在子shell中执行(cd /boot;ls) (umask 077; touch file1000)

注意:如何查看当前子shell ps | grep $$ $$:当前shell的pid

{} 集合 touch file{1..9}

\ 转义符,让元字符回归本意

3、shell脚本文件 

一般以.sh结尾的文件 .py结尾的文件是python的脚本

第一行一般指定解释器

#!/bin/bash      #!/usr/bin/env bash

注释 :以#开有的都不生效

执行shell脚本

1) 直接执行,在命令行写脚本的路径 开启子shell执行,将执行的结果返回到父shell中,前提,脚本要有可执行权限

2) 调用解释器执行

3) source 脚本的绝对路径,点执行 . 脚本的绝对路径 直接在当前shell下执行,会影响当前的shell环境

脚本测试

sh –x script

这将执行该脚本并显示所有变量的值

•sh –n script

不执行脚本只是检查语法模式,将返回所有错误语法

•sh –v script

执行脚本前把脚本内容显示在屏幕上

 三、shell变量

1、变量

1)用一个固定的字符串去表示不固定的内容

2)在内存中开辟一个空间来储存某些变化的值,可以对这个空间进行命名,这个名称就是变量名。 

3)变量名=变量值

2、 shell变量类型

四类:自定义变量、环境变量、位置变量、预定义变量


1)自定义变量

变量名=变量值 变量名必须以字母或下划线开头,区分大小写 ip1=192.168.2.115

引用变量: $变量名 或 ${变量名}

查看变量: echo $变量名 set(所有变量:包括自定义变量和环境变量)

取消变量: unset 变量名

作用范围: 仅在当前shell中有效

2)环境变量

定义环境变量: 方法一 export back_dir2=/home/backup

方法二 export back_dir1 将自定义变量转换成环境变量

引用环境变量: $变量名 或 ${变量名}

查看环境变量: echo $变量名

取消环境变量: unset 变量名

变量作用范围: 在当前shell和子shell有效

环境变量拥有可继承性:export之后就拥有继承性

永久生效:写到四个登录脚本,

1)/etc/profile

2)/etc/bashrc

3)~/.bash_profile //配置roo用户的环境变量

4)~/.bashrc   /home/tom/.bashrc ///配置tom用户的环境变量

所有用户登录都会加载这四个配置文件

/etc下两个为全局环境配置文件,对所有用户生效

家目录下两个为用户自己的环境配置文件,只对用户本人生效

举例:

vim /etc/profile

JAVA_HOME=/usr/local/java

PATH=$JAVA_HOME/bin:$PATH

export JAVA_HOME PATH


/etc/profile

这是系统最主要的shell设置文件,也是用户登陆时系统最先检查的文件,有关重要的环境变量都定义在此,其中包括PATH,USER,LOGNAME,MAIL,HOSTNAME,HISTSIZE,INPUTRC等。而在文件的最后,它会检查并执行/etc/profile.d/*.sh的脚本。

~/.bash_profile

这个文件是每位用户的bash环境设置文件,它存在与于用户的主目录中,当系统执行/etc/profile 后,就会接着读取此文件内的设置值。在此文件中会定义USERNAME,BASH_ENV和PATH等环境变量,但是此处的PATH除了包含系统的$PATH变量外加入用户的“bin”目录路径.

~/.bashrc

接下来系统会检查~.bashrc文件,这个文件和前两个文件(/etc/profile 和~.bash_profile)最大的不同是,每次执行bash时,~.bashrc都会被再次读取,也就是变量会再次地设置,而/etc/profile,~./bash_profile只有在登陆时才读取。就是因为要经常的读取,所以~/.bashrc文件只定义一些终端机设置以及shell提示符号等功能,而不是定义环境变量。

~/.bash_login

如果~/.bash_profile文件不存在,则系统会转而读取~/.bash_login这个文件内容。这是用户的登陆文件,在每次用户登陆系统时,bash都会读此内容,所以通常都会将登陆后必须执行的命令放在这个文件中。

.profile

如果~./bash_profile ~./bash_login两个文件都不存在,则会使用这个文件的设置内容,其实它的功能与~/.bash_profile相同。

.bash_logout

如果想在注销shell前执行一些工作,都可以在此文件中设置。

例如:

#vi ~.bash_logout

clear

仅执行一个clear命令在你注销的时候

~/.bash_history

这个文件会记录用户先前使用的历史命令。

注意:在/etc/profile.d建立独立的环境变量配置文件

常用环境变量:USER UID HOME HOSTNAME PWD PS1 PATH

PATH:这个变量存放的是所有命令所在的路径 修改:PATH=$PATH:+目录

意义:让所有命令在执行的时候不必输入路径


3)位置变量

位置变量用于命令行、函数或脚本中传递参数。

执行脚本时,通过在脚本后面给出具体参数(多个参数空格隔开)对相应位置变量进行赋值。

$0表示命令本身,$1-$9代表1-9个参数,$10以上需要{}括起来,如${10}

4)预定义变量

$0 脚本名

$* 所有的参数,横的显示

$@ 所有的参数,竖的显示

$# 参数的个数

$$ 当前进程的PID

$! 上一个后台进程的PID

$? 上一个命令的返回值 0表示成功 ,1-254 表示不成功

 3、变量的赋值

1)直接赋值

a=3

2)从键盘赋值

read -p [提示信息]:[变量名] 

3)使用命令行参数赋值(利用位置变量传参)

4)利用命令的输出结果赋值

在需要复制到语句中使用反单引号

5)从文件中读取数据进行赋值

定义或引用变量时注意事项:

" " 弱引用 可以实现变量和命令的替换

' ' 强引用 不完成变量替换

` ` 命令替换 等价于 $() 反引号中的shell命令会被先执行

4、shell变量的运算

shell是弱语言,shell中变量值的类型默认全部都是字符串,不能直接运算,如需运算需要使用特殊方法。

1. 整数运算

方法一:expr

expr 1 + 2

expr $num1 + $num2

运算符 :+ - \* / %

方法二:$(())

echo $(($num1+$num2))

echo $((num1+num2))

echo $((5-3*2))

echo $(((5-3)*2))

echo $((2**3))

sum=$((1+2)); echo $sum

运算符:+ - * / %

方法三:$[]

echo $[5+2]

echo $[5**2]

运算符: + - * / %

方法四:let

let sum=2+3; echo $sum

let i++; echo $i

2. 计算小数

echo 1.5+1.5 | bc

3.0 //会显示跟计算的小数点一样的位数,bc命令没有的话,安装 yum install bc -y

四、shell条件测试 

1、文件测试

语法:test   条件表达式

           test -f file && echo true ||  echo false

           [ 条件表达式 ] 两边要空格

           [ -f file ] && echo true || echo false

           [[ 条件表达式 ]]    支持正则

           if [[ $1 =~ ^[0-9]+$ ]] then ... else... fi

文件测试操作符:

-d 测试是否为目录

-a  测试目录或文件是否存在

-f   测试是否为文件

-r   测试当前用户是否可读

-w  测试当前用户是否可写

-x   测试当前用户是否可执行

2、整数测试

 整数测试常用于数值之间的运算

语法:[ 整数1 操作符 整数2 ]

整数测试操作符:

-eq 等于

-ne 不等于

-gt 大于

-lt 小于

-le 小于或等于

-ge 大于或等于

3、字符串测试 

包括比较字符串是否相同,测试字符串长度是否为0.

语法:[ 字符串 1 = 字符串2 ] 

字符串测试运算符:

-z 判断字符串长度是否为0

-n 判断字符串长度是否为非0

!= 判断两个字符串是否不相等

=判断两个字符串是否相等

4、if条件语句 

1)单分支

if [ 条件表达式 ] 

    then 

            代码块

fi

2)双分支

if [ 条件表达式 ]

   then

          代码块

else

       代码块

fi

3)多分支

if [ 条件表达式 ]

   then

          代码块

elif

          代码块

else

           代码块

fi

4)多重条件判断

条件表达式1 选项 条件表达式2

选项:

-a:并且

-o :或者

! 条件表达式 :取反,非

if [ $username = "root" -a $password = "redhat" ]

if [ $username = "root" ] && [ $password = "redhat" ]

if [[ $username = "root" && $password = "redhat" ]]

5、case条件语句 

case看起来比if语句更加工整,企业在实现系统服务启动脚本时偏向于case。

case 变量值 in

  条件表达式1 )

           代码块1

;;

条件表达式2)

           代码块2

;;

条件表达式3)

            代码块3

;;

*)

              无匹配后代码块

esac

条件表达式匹配:

* 任意字符

? 任意单个字符

[abc] a、b、c其中之一

[a-n] 从a到n中任意一个

| 多重选择

五、shell循环 

1、 for循环

for循环主要用于固定次数的循环,不能用于守护进程及无限循环。

语法结构:

1)

for 变量名 in 取值列表

      do 

              循环体

       done

2)

for ((i=1;i<=10;i++))

do

动作块

done

 注意:for循环读文件时是以空格为分隔符开始读的,如不想以空格为分割符,可以在脚本中自定义分割符IFS=$''/n(以换行符为分割符)。

2、while循环

语法:

while 条件测试

do

       循环体

done

while循环语句会对条件测试进行判断,如果条件成立时,则执行do和done之间的循环体,直到条件测试不成立时才停止循环。

注意:空命令":"与true命令作用相同,在while循环和if/then中也可以使用。如下: 

1)while死循环

while : 

   do

      代码块

done

2)if/then中引出分支

if  条件

then  :  #什么也不做,引出分支

else

      action

fi

3、for循环与while循环比较

1)for循环写ping脚本

#!/bin/bash

for i in {2..254}

do

 {

      ip=192.168.100.$i

      ping -c 3 $ip &>/dev/null

      if [ $? -eq 0]

      then

           echo "$ip is up."

  }&

done

wait

echo "all finish..."

2)while循环写ping脚本

#!/bin/bash

i=2

while [ $i -le 254 ]

do

   {

       ip=192.168.100.$i

       ping -c 3 $ip&>/dev/null

       if[ $? -eq 0 ]

       then 

            echo "$ip is up."

       fi

    }&

let i++

done

wait

echo "all finish..."

4、expect

expect是一种编程语言,用来实现自动和交互任务进行通信,需要指定#!/usr/bin/expect来进行解释。如在shell脚本中进行使用1.在脚本中进行expect脚本调用

/usr/bin/expect script.exp 2.把expect代码放在/usr/bin/expect <<EOF    代码     EOF 中进行使用。

expect实现批量主机公钥推送: 

 #!/bin/bash

#检测是否安装expect软件

rpm -ne expect &>/dev/null

if [ $? -eq 0 ];then

       yum -y install expect

    if [ $? -eq 0 ];then

        echo "install success!"

     else 

         echo "install fail!"

         exit 2

    fi

fi

#检查客户端是否生成公私钥

if [ !-f ~/.ssh/id_rsa ];then

    ssh-keygen -p "" -f~/.ssh/id_rsa

    if [ $? -eq 0 ];then

        echo "success!"

    else

        echo "fail!"

        exit 2

     fi

fi

#检查客户端是否可以ping通,ping通推送密钥

>ip.txt

 for i in {2..254}

 do

   {

     ip=192.168.100.$i

     ping -c1 -w1 $ip &>/dev/null

     if [ $? -eq 0 ];then

           echo "$ip">>ip.txt

     #ping通一个推送一个

     /usr/bin/expect<<-EOF

     set timeout 10

     spawn ssh-copy-id $ip  

     expect {

      "yes/no"{send "yes\r";exp_contiue}

      "passwd:"{send "123\r" }

        }

    expect eof

   EOF

fi

    }&

done

wait

echo "finish..."

 注意:ssh-keygen -p(指定密码为空)-f(指定保存密钥的文件名)

5.seq

seq 1 2 10 //打印奇数 起始位置 步长 结束位置步长

seq 5 10 //打印5到10 起始位置 结束位置 ,步长为1

seq -w 10 //等宽显示 :不够位数在前面补0

-s指定输出的分隔符,默认为\n,即默认为回车换行

六、shell数组

1、数组概念

 数组中有限个相同类型的变量用一个名字命名,然后用编号区分它们。用于区分不同元素的编号称为数组下标,数组的元素有时也称为下标变量。

2、shell数组的类型 

数组分为普通数组和关联数组。

普通数组:数组元素的索引(下标)从0开始编号,获取数组中的元素要利用索引(下标)。索引可以是算数表达式,其结果必须是一个整数。

普通数组的定义:

books=(Linux shell awk)#定义普通数组,python中称为列表

关联数组:关联数组跟普通数组的不同是,他的索引下标是任意的整数和字符串。

 关联数组的定义:

info=([name]=linux [sex]=male)

#在python中称为字典

索引下标为字符串不必为整数,如:name sex

3、定义数组的类型 

用户定义的是普通数组,如需使用关联数组需先声明再使用。声明关联数组使用-A参数,通常情况下shell解释器隐式声明普通数组,用户无需操作。

声明普通数组:declare -a array

声明关联数组:declare -A array

4、数组的定义 

1)直接定义

数组名=(变量值1 变量值2 变量值3 变量值4...)

2)下标定义数组

数组名=([下标1]=变量值1 [下标2]=变量值2...)

例:

declare -A info1

info1=([name]=Linux [age]=18)

echo ${info1[age]}

3)间接定义数组

数组名[下标]=变量值1;数组名[下标]=变量值2

如:array[0]=linux

4)从文件中读入定义数组

数组名=($(`变量值`))或数组名=(`变量值`)

如:

array=(`cat /etc/passwd`)

echo ${array[*]}

5、访问数组的方式 

echo ${!array[*]}  访问数组的所有索引

echo ${!array[@]}  访问数组的所有索引

echo ${array[*]}  访问数组所有值

echo ${array[@]}  访问数组所有值

echo ${#array[@]} 统计数组元素个数

echo ${array[0]}  访问数组中第一个元素

6、数组的遍历

 1)while

#!/bin/bash

while read line

do

  hosts[++i]=$line

done</etc/hosts

for i in ${!hosts[@]}

do

   echo "$i:${hosts[i]}"

done

2)for

#!/bin/bash

IFS=$'\n'

for i in  `cat /etc/hosts`

do

   hosts[++j]=$line

done

for i in ${!hosts[@]}

do

   echo "$i:${hosts[i]}"

done

7、数组的删除

unset数组[下标 ]删除相应数组元素,不带下标,表示删除整个数组的所有元素

unset array[1]

8、使用数组统计TCP连接状态 

#!/bin/bash

while true

do

  unset status

  declare -A status

 type=`ss -an|grep :80` |awk '{print $2}'

for j in ${!status[@]}

do

  echo "$j:${status[$j]}"

done

sleep 1

clear

done 

七、shell函数 

1、shll函数的语法

函数名(){

      函数要实现功能的代码

}

或:

function 函数名(){

       函数要实现功能的代码

}

2、函数的调用 

函数只有被调用才能被执行,调用指定的函数名,函数出现的地方会被自动替换为函数代码。

如:在脚本中使用,放在只包含函数的单独文件中使用,交互环境中使用。

最基本语法格式:

   函数名

单参数的语法格式:

 函数名 参数1 参数2

3、函数的返回值介绍 

默认情况下,在执行完函数最后一行代码后,函数最终会返回一个状态数字,这个时候可以使用$?一个变量来查看函数执行状态,如返回0表示方法正常退出,非0表示程序发生错误或非正常退出。使用 return(范围0-255)关键字来返回一个整数,其作用是退出函数。

如想返回字符串或其他类型,可以使用echo变量来返回值。                                          

 示例:

#!/bin/bash

num=(1 2 3)

num2=(2 3 4)

array() {

      local newarray=($*)

      local i

      for ((i=0;i<$#;i++))

        do

                newarray[$i]=$( ( ${newarray[$i]}*5 ) )

       done

      echo "${newarray[*]}"

}

result=`array ${num[*]}`

echo ${result[*]}

 

result=`array ${num2[*]}`

echo ${result[*]}

4、循环结构中break,continue,return,exit的区别 

break:应用于switch语句和循环语句中,break命令表示直接退出循环,执行循环结构下面的第一条结构。

continue:用于for、while、repeat语句中结束循环内的本次处理,继续从循环体的开始位置继续执行。

return:在函数中将数据返回,或返回一个结果给调用函数的脚本,只退出函数,不退出脚本。

exit:退出当前shell程序,退出脚本。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值