Linux基础篇学习—shell及shell脚本

概述

shell概念

shell又称命令解释器,它能识别用户输入的各种命令,并传递给操作系统

它的作用类似于Windows操作系统中的命令行,但是,Shell的功能远比命令行强大的多;在UNIX或者localhost中,Shell既是用户交互的界面,也是控制系统的脚本语言

shell的种类

CentOS linux系统默认的shell为bash

shell 相关
Bourne Shell 标识为sh,该Shell由Steve Bourne在贝尔实验室时编写。在许多Unix系统中,该Shell是root用户的默认的Shell。
Bourne-Again Shell 标识为bash,该Shell由Brian Fox在1987年编写,是绝大多数linux发行版的默认的Shell。
Korn Shell 标识为ksh,该Shell由贝尔实验室的David Korn在二十世纪八十年代早期编写。它完全向上兼容 Bourne Shell 并包含了C Shell 的很多特性。
C Shell 标识为csh,该Shell由Bill Joy在BSD系统上开发。由于其语法类似于C语言,因此称为C Shell。

bash shell特性

  1. 历史命令 history
  2. 命令和文件补全 Tab
  3. 命令别名 alias
  4. 作业控制 job(前台fg,后台bg)
  5. 程序脚本
  6. 通配符
  7. 管道 | tee

shell相关

查看系统shell cat /etc/shells
查看系统默认shell echo $SHELL
查看系统的bash版本 bash --versionrpm -qa bash
检查bash是否安全

老版本的bash存在较为严重的安全信息,凭借漏洞,攻击者可能会接管计算机的整个系统

[root@zycentos7 ~]# env x='(){:;};echo be careful ' bash -c "echo this is a test"
this is a test

如果返回如下内容,则需要升级bash yum -y update bash

be careful
this ia a test

shell脚本概念

命令或程序语句不在命令行下执行,而是通过一个程序文件来执行,该程序文件就是脚本

常见的脚本语言及其优点

语言 优点
php语言 优势在于小型网站系统的开发
perl语言 优势在于开发较为复杂的运维工具软件、web界面的管理工具和web业务的开发(例如:跳板机、批量管理软件saltstack等)
python语言 上层语言,类似于Java,go等编程语言(cmdb自动化运维平台,openstack)
shell语言 优势在于处理偏操作系统底层的业务,使用shell更符合liunx运维简单、易用、高效的原则

shell脚本的建立与执行

shell脚本运行时首先查找系统环境变量ENV,该变量制定了环境文件(加载顺序通常是/etc/profile 、~/.bash_profile 、~/.bashrc 、/etc/bashrc等),在加载了以上环境变量文件后,shell就开始执行shell脚本中的内容

shell脚本的运行

  1. sh test.shbash test.sh
    脚本文件本身没有执行权限或者脚本文件开头没有指定解释器时使用
    执行脚本都会启动新的子shell执行,执行完后,退回到父shell
  2. ./test.shpath/test.sh
    指定在当前目录下执行脚本,但是脚本需要有执行权限
  3. source test.sh.test.sh
    使用source或‘.’ 读入或加载指定的shell脚本文件,然后依次执行指定的shell脚本文件中的所有语句
    在当前父shell脚本进程中运行,结束后脚本中的变量(函数)值在当前shell中依然存在
  4. sh < test.shcat test.sh | sh :
    sh可接收标准输入
[root@zycentos7 ~]# cat a.sh
userdir=`pwd`
[root@zycentos7 ~]# sh a.sh
[root@zycentos7 ~]# echo $userdir

[root@zycentos7 ~]# source a.sh
[root@zycentos7 ~]# echo $userdir
/root

shell脚本注释

  1. 单行注释
    #
  2. 多行注释
    :< …注释内容
    BLOCK

shell脚本规范

  1. 开头指定shell解释器
    #!bin/bash 其他#行表示注释
    名称见名知义,backup_mysql.sh
    尽量不用中文注释
  2. 开头加版本版权等信息
    ~.vimrc配置,代码缩进
  3. 多使用内部命令
    提高性能
    type查看
  4. 不用cat,少用管道
  5. 仔细阅读出错信息
  6. 仔细阅读出错信息
  7. 脚本以.sh为扩展名

变量

变量类型

可以在创建变量的shell以及派生出来的任意子进程shell中使用,又可分为:自定义环境变量和bash内的环境变量

环境变量(全局变量)

环境变量的名字均采用大写形式

普通变量(局部变量)

只能在创建他们的shell函数或shell脚本中使用

变量的导出

set 输出所有变量
env 输出全局变量
declare 输出所有变量、函数、和已经导出的变量

自定义全局变量

语法

export NAME=value
[root@zycentos7 ~]# declare -x JOB=linux
[root@zycentos7 ~]# declare|grep JOB
JOB=linux
_=JOB=linux
[root@zycentos7 ~]# declare|grep JOB
JOB=linux

自定义局部变量

[root@zycentos7 ~]# a="BeiJing TIME:`date`"	;反引号
[root@zycentos7 ~]# echo $a
BeiJing TIME:Sun Dec 8 15:29:53 CST 2019
[root@zycentos7 ~]# a='BeiJing TIME:"date"'	;双引号,弱引用
[root@zycentos7 ~]# echo $a
BeiJing TIME:"date"
[root@zycentos7 ~]# a='BeiJing TIME:'date''	;单引号,强引用,所见即所得
[root@zycentos7 ~]# echo $a
BeiJing TIME:`date`
[root@zycentos7 ~]# CMD=$(pwd)
[root@zycentos7 ~]# echo $CMD
/root

变量永久生效

  1. 局部变量保存位置,当前用户有效
    ~.bash_profile
    .bashrc
  2. 全局变量保存位置,全局有效
    /etc/profile
    /etc/profile.d/
    /etc/bashrc

设置登录提示

  1. vim /etc/motd
    方法二:
  2. vim /etc/profile.d/zhao.sh

环境变量初始化与对应文件的生效顺序

linux系统bash加载环境变量的过程
系统运行shell的方式
(1)通过系统用户登录后默认运行的shell
(2)非交互式运行Shell
(3)执行脚本运行非交互式shell

[root@zycentos7 ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin

bash shell 相关运算

1. 生成加密密码

Centos 6 grub-md5-cryp
Centos 7 grub2-mkpasswd-pbkdf2

2. 生成自然数

$ Number 第Number个参数
$# 参数个数

echo输出自然数

[root@zycentos7 ~]# echo {0..10}
0 1 2 3 4 5 6 7 8 9 10
eval

eval会对后面的cmdLine进行两遍扫描,如果在第一遍扫面后cmdLine是一个普通命令,则执行此命令;如果cmdLine中含有变量的间接引用,则保证简介引用的语义

eval生成随机数

[root@zycentos7 ~]# cat test.sh
END=5
for i in `eval echo {1..$END}`
do
echo $i
done
[root@zycentos7 ~]# sh test.sh
1
2
3
4
5

测试eval

[root@zycentos7 ~]# set 11 22 33 44
[root@zycentos7 ~]# echo $4
44
[root@zycentos7 ~]# echo $#
4
[root@zycentos7 ~]# echo "$$#"
7107#
[root@zycentos7 ~]# echo "\$$#"
$4
[root@zycentos7 ~]# eval echo "\$$#"
44
seq 产生从某个数到另一个数之间的所有整数
seq [选项]... 尾数
seq [选项]... 首数 尾数
seq [选项]... 首数 增量 尾数

seq -w 在列前添加0,使得宽度相同

[root@zycentos7 ~]# seq -w 98 100
098
099
100

seq -f 使用printf 样式的浮点格式

-f"%#g"(补空格)
-f"%0#g"(补0)
[root@zycentos7 ~]# seq -f"%4g" 8 12
   8
   9
  10
  11
  12
[root@zycentos7 ~]# seq -f"%04g" 8 12
0008
0009
0010
0011
0012

-s 指定分隔符(默认使用\n)换行\n无效

[root@zycentos7 ~]# seq -s" " -f"str%02g" 9 11
str09 str10 str11
[root@zycentos7 ~]# seq -s"`echo -e "\t"`" -f"str%02g" 9 11
str09   str10   str11

3.生成随机数

(1)通过内部系统变量($RANDOM) echo $RANDOM
(2)使用awk的随机函数 awk 'BEGIN{srand();print rand()*1000000}'
(3)openssl rand产生随机数 openssl rand -base64 8

1.八位字母和数字的组合
openssl rand -base64 8|md5sum|cut -c 1-8
2.八位数字
openssl rand -base64 8|cksum|cut -c 1-8

(4)通过时间获得随机数(date)

1.生成19位数字
date +%s%N  #
2.取八位数字
date +%s%N|cut -c 6-13
13578053
3.八位字母和数字的组合
date +%s%N|md5sum|head -c 8

(5)通过系统内唯一数据生成随机数

1./dev/random存储系统当前运行的环境的实时数据,可以看作系统某时候的唯一值数据,提供优质随机数
2./dev/urandom是非阻塞的随机数产生器,读取时不会产生阻塞,速度更快、安全性较差的随机数发生器

1.生成数字和字母混合的随机字符串
cat /dev/urandom|head -n 10|md5sum|head -c 10
【dc32c5047f】
2.生成全字符的随机字符串
cat /dev/urandom|strings -n 8|head -n 1
【}pFYi%%D~】
3.生成数字加字母的随机字符串,其中 strings -n设置字符串的字符数,head -n设置输出的行数
cat /dev/urandom|sed -e 's#[^a-zA-Z0-9]##g'|strings -n 8|head -n 1
【aPdKtMod】
4.生成全数字的随机字符串
head -200 /dev/urandom|cksum|cut -d " " -f1
【1182233652】

(6)读取Linux的uuid码

UUID码全称是通用唯一识别码 (Universally Unique Identifier, UUID),UUID格式是:包含32个16进制数字,以“-”连接号分为五段,形式为8-4-4-4-12的32个字符,linux的uuid码由内核提供,在/proc/sys/kernel/random/uuid文件内。

cat/proc/sys/kernel/random/uuid 每次获取到的数据都会不同

1.获取不同的随机整数
cat /proc/sys/kernel/random/uuid |cksum|cut -f1 -d " "  #
【3838247832】
2.数字加字母的随机数
cat /proc/sys/kernel/random/uuid |md5sum|cut -c 1-8  #
【6092539b】

(7)从元素池中随机抽取

pool=(a b c d e f g h i j k l m n o p q r s t 1 2 3 4 5 6 7 8 9 10)
num=KaTeX parse error: Expected '}', got '#' at position 2: {#̲pool[*]} result…{pool[$((RANDOM%num))]}
用于生成一段特定长度的有数字和字母组成的字符串,字符串中元素来自自定义的池子

[root@zycentos7 ~]# cat seqeand.sh
length=8 
i=1 
   
seq=(0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) 
   
num_seq=${#seq[@]} 
   
while [ "$i" -le "$length" ] 
do 
 seqrand[$i]=${seq[$((RANDOM%num_seq))]} 
 let "i=i+1" 
done 
   
echo "The random string is:" 
for j in ${seqrand[@]} 
do 
 echo -n $j 
done 
echo 
[root@zycentos7 ~]# sh seqeand.sh
The random string is:
j2dZnA5r

4.生成随机UUID

[root@zycentos7 ~]# cat /proc/sys/kernel/random/uuid
75052f89-9b32-4dcd-8deb-146a616be19e

5.生成随机MAC地址

(1)echo "00:60:2F$(dd bs=1 count=3 if=/dev/random 2>/dev/null |hexdump -v -e '/1 ":%02X"')"
(2)echo "$(hexdump -n3 -e'/3 "00:60:2F" 3/1 ":%02X"' /dev/random)"
(3)printf '00:60:2F:%02X:%02X:%02X\n' $[RANDOM%256] $[RANDOM%256] 
(4)echo 00:60:2f:`openssl rand -hex 3 | sed 's/\(..\)/\1:/g; s/.$//'`

6.命令排序

; 不具备逻辑判断
&& 前一个命令执行成功才继续执行下一个命令
|| 前一个命令执行的结果不影响下一个命令

command & 后台执行
command &>/dev/null 混合重定向(标准输出1,错误输出2)
command1 && command2 命令排序,逻辑判断

shell脚本进阶

1.shell脚本之特殊变量

常用的特殊位置参数变量说明

位置变量 作用说明
$0 获取当前执行的Shell脚本的文件名,如果执行脚本包含了路径,那么就包括脚本路径
$n 获取当前执行的Shell脚本的第n个参数值,n=1…9,n大于9用大括号括起来,例如${10},接的参数以空格隔开
$# 获取当前执行的Shell脚本后面接的参数的总个数
$*或$@ 获取当前Shell脚本所有传参参数
“$*” 将所有的参数视为单个字符串,相当于"$1 $2 $3"
“$@” 将所有的参数视为不同的独立字符串,相当于"$1" “$2” “$3”…"

$@和"$@“等价,$*和”$*" 不等价
"$@"是将多参数传递给其他程序的最佳方式,因为它会保留所有的内嵌在每个参数里的任何空白

[root@zycentos7 ~]# dirname /root/test/test.sh
/root/test
[root@zycentos7 ~]# basename /root/test/test.sh
test.sh

"$*“和”$@"的区别测试

[root@zycentos7 ~]# set -- This "is a" test
[root@zycentos7 ~]#  for i in "$@";do echo $i;done
This
is a
test
[root@zycentos7 ~]#  for i in "$*";do echo $i;done
This is a test

$*和"$*"的区别

$* 参数单独列出
"$*" 所有参数当成一个整体
n=0
for i in $*
do
    echo $i
    let n++
done
echo $n
echo "---------------------"
for j in "$@"
do
    echo $j
done

Shell进程的特殊状态变量说明

位置变量 作用说明
$? 获取执行上一个指令的执行状态返回值(0成功,非0为失败)
$$ 获取当前执行的Shell脚本的进程号(PID)
$! 获取上一个在后台工作的进程的进程号(PID)
$_ 获取在此之前执行的命令或脚本的最后一个参数
[root@zycentos7 ~]# cat c.sh
read -p "please input your number:" num
if [ 5 -eq $num ]
then
    echo "$num"
    exit 10
elif [ 6 -eq $num ]
then
    echo "$num"
    exit 20
else
    echo "$num"
    exit 30
fi
[root@zycentos7 ~]# sh c.sh
please input your number:4
4
[root@zycentos7 ~]# echo $?
30
[root@zycentos7 ~]# ls -l
[root@zycentos7 ~]# echo $_
-l

在企业场景下,“$?”返回值的用法如下:

  1. 判断命令、脚本或函数等程序是否执行成功
  2. 若在脚本中调用执行“exit 数字”,则会返回这个数字给“$?”变量
  3. 如果是在函数里,则通过“return 数字”把这个数字以函数返回值的形式传给“$?"

练习 【$$的企业级应用】实现系统中多次执行某一个脚本后的进程只有一个

说明:有时执行定时任务脚本的频率比较快,并不知道上一个脚本是否真的执行完毕,但是,业务要求同一时刻只能有一个同样的脚本在运行,此时就可以利用$$获取上一次运行的脚本进程号,当程序重新运行时,根据获得的进程号,清理掉上一次的进程,运行新的脚本命令

2.shell的内置变量

内部命令:echo ,eval,exec,export,read,shift

echo

echo参数选项 说明
-n 不换行输出内容
-e 解析转义字符
转义字符 说明
\n 换行
\r 回车
\t 制表符(tab)
\b 退格
\v 纵向制表符

exec

exec命令能够在不创建新的子进程的前提下,转去执行指定的命令,当指定
的命令执行完毕后,该进程( 也就是最初的Shell)就终止了

[zhao@zycentos7 ~]$ exec date	;root用户执行会退出登录
Wed Dec 18 08:55:46 CST 2019
exec < /etc/passwd
while read line
do
    echo $line
done

当使用exec打开文件后,read 命令每次都会将文件指针移动到文件的下一行进行读取,直到文件末尾,利用这个可以实现处理文件内容

read

read -p

shift

shift 语句会按如下方式重新命名所有的位置参数变量

即$2成为$1、$3成为$2等,以此类推
在程序中每使用一次shift 语句,都会使所有的位置参数依次向左移动一个位置,并使位置参数$#减1,直到减到0为止

应用场景 当我们写Shell希望像命令行的命令通过参数控制不同的功能时,就会先传一个类似-c的参数,然后再接内容

exit

退出Shell程序,在exit之后可以有选择地指定一个数位作为返回状态

3.shell脚本之子串及特殊变量

表达式 说明
${parameter} 返回字符串的内容
${#parameter} 统计字符串长度
${parameter:offset} 从第offset个字符开始输出
${parameter:offset:length} 从第offset个字符开始输出length个字符
${parameter#word} 从开头开始删除最短匹配的字符串
${parameter##word} 从开头开始删除最长匹配的字符串
${parameter%word} 从结尾开始删除最短匹配的字符串
${parameter%%word} 从结尾开始删除最长匹配的字符串
${parameter/pattern/string} 使用string代替第一个匹配的pattern
${parameter//pattern/string} 使用string代替所有匹配的pattern

实例1 统计字符串a的长度

echo  ${#a}	;速度最快
expr length "$a"
echo $a|wc -L
echo $a|awk '{print length($0)}'

实例2 批量修改文件名:将testXX.txt修改为ybcXX.txt

[root@zycentos7 ~]# touch file/test{01..20}.txt
[root@zycentos7 ~]# cat mv.sh
for i in `ls file`
do
    echo $i
    mv file/$i file/${i/test/ybc}
done
[root@zycentos7 ~]# sh mv.sh

实例3 打印下面语句中字符数小于6的单词

I am oldboy linux,welcome to our training.
[root@zycentos7 ~]# cat cut.sh
for i in `cat a.txt`
do
    #echo ${#i}
    if [ ${#i} -lt 6 ]
    then
        echo $i
    fi
done
[root@zycentos7 ~]# sh cut.sh
I
am
to
our

shell的特殊扩展变量

表达式 说明
{parameter:-word} parameter有值输出值,无则输出word
{parameter:=word} parameter有值输出值,无则输出word,并将word赋值给parameter
{parameter:?word} parameter值为空,word作为标准错误输出
{parameter:+word} parameter值不为空,输出时word替换其值

实例 删除7天的过期备份数据

DIR_BACK=backup
find ${DIR_BACK:-/backup} -type f -mtime +7 -exec rm -rf {} \;

防止忘记定义DIR_BACK变量而导致异常

4.shell脚本之数值运算

常见的算术运算符

+、-、、/、%、++、–、&&、||、!、**(幂运算)
比较符号:==、!=、=(对于字符串表示相当于)
赋值符号:=、+=、-=、
=、/=、%=

常见的算术运算命令

运算操作符与运算命令 意义
(()) 用于整数运算的常用运算符,效率很高
let 用于整数运算,类似于“(())’
expr 可用于整数运算,但还有很多其他的额外功能
bc Linux下的一个计算器程序(适合整数及小数运算)
$[] 用于整数运算
awk awk既可以用于整数运算,也可以用于小数运算
declare 定义变量值和属性,-i 参数可以用于定义整形变量,做运算
let

语法

let 赋值表达式

let赋值表达式的功能等同于“((赋值表达式))”
范例5-12:监控Web服务状态,如果访问两次均失败,则报警(let 应用案例)。

expr

要注意,在使用expr时:

  1. 运算符及用于计算的数字左右都至少有一个空格,否则会报错
  2. 使用乘号时,必须用反斜线屏蔽其特定含义,因为Shell可能会误解星号的含义

expr命令用途:
(1)整数运算
(2)字符串匹配
(3)字符串长度计算

双小括号 “(())” 的操作方法

运算操作符与运算命令 意义
((i=i+1)) 此种书写方法为运算后赋值法,即将i+1的运算结果赋值给变量i,不能用“echo ((i=i+1))”的形式输出表达式的值,但可以用echo $((i=i+1))输出其值
i=$((i+1)) 可以在“(())” 前加$符,表示将表达式运算后赋值给i
((8>7&&5==5)) 可以进行比较操作,还可以加入逻辑与和逻辑或,用于条件判断
echo $((2+1)) 需要直接输出运算表达式的运算结果时,可以在“(O)" 前加$符

实例1 判断一个变量值或字符串是否是整数

read -p "num1:" num1
read -p "num1:" num2
expr $num1 + 1 >/dev/null 2>&1
if [ $? -ne 0 ]
then
    echo "请您输入一个正确的整数.."
    exit
fi
expr $num2 + 1 >/dev/null 2>&1
if [ $? -ne 0 ]
then
    echo "请您输入一个正确的整数.."
    exit
fi
echo "$num1 + $num2 = `expr $num1 + $num2`"
echo "$num1 - $num2 = `expr $num1 - $num2`"
echo "$num1 * $num2 = `expr $num1 \* $num2`"
echo "$num1 / $num2 = `expr $num1 / $num2`"

实现原理:利用以expr作运算时变量或者字符串必须是整数的规则,把一个变量或字符串和一个已知的整数(非0)相加,看命令返回值是否为0,为0,则认为做加法的变量或字符串为整数,否则就不是

实例2 判断文件扩展名是否符合要求

if expr "$1" : ".*\.pub" >/dev/null 2>&1
then
    echo "真.."
else
    echo "假.."
fi

5.shell脚本之条件测试

常用语法

条件测试语法 说明
test <测试表达式> test 命令和“<测试表达式>”之间至少有一个空格
[ <测试表达式> ] []的边界和内容之间至少有一个空格
[[ <测试表达式> ]] [[]] 的边界和内容之间至少有一个空格
((< 测试表达式>)) 一般用于if语句,(()) 两端不需要有空格

1.在[]中可以使用通配符等进行模式匹配
2.&&、|、>、<等操作符可以应用于[[]]中,但不能应用于[]中,在[]中一般用-a、-o、-gt (用于整数)、-It (用于整数)代替上述操作符
3.对于整数的关系运算,也可以使用Shell 的算术运算符(())

文件测试表达式

常用文件测试操作符 说明
-d 文件存在且为目录则为真
-f 文件存在且为普通文件则为真
-e 文件存在则为真,注意区别于“-f”, -e不辨别是目录还是文件
-r 文件存在且可读则为真
-s 文件存在且文件大小不为0则为真
-w 文件存在且可写则为真
-x 文件存在且可执行则为真
-L 文件存在且为链接文件则为真
fl -nt f2 文件fl比文件f2新则为真(修改时间)newer than
fl -otf2 文件fl比文件f2旧则为真(修改时间)older than

字符串测试表达式

常用字符串测试操作符 说明
-n “字符串” 字符串的长度不为0则为真 no zero
-z “字符串” 字符串的长度为0则为真 zero
"串1" = "串2",可使用"=="代替"="
"串1" != "串2",不可使用"!=="代替"="

对于字符串的测试,一定要将字符串加双引号之后再进行比较,如[ -n “$myvar” ],特别是使用[]的场景,字符串比较时若等号两端没有空格,则会导致判断出现逻辑错误

整数二元比较操作符

if条件命令选项 含义
-eq 比较两个参数是否相等(例如,if [ 2 –eq 5 ])
-ne 比较两个参数是否不相等
-lt 参数1是否小于参数2
-le 参数1是否小于等于参数2
-gt 参数1是否大于参数2
-ge 参数1是否大于等于参数2

逻辑操作符

if条件命令选项 含义
-a 与,两端都为真,结果为真
-o 或,两端有一个为真,结果为真
两端相反,结果为真

练习1 条件测试的方法

  1. test条件测试

如果文件存在,输出true,否则输出false

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

测试字符串是否为0

[root@zycentos7 ~]# test -z "oldboy" && echo 1 || echo 0
0
[root@zycentos7 ~]# test -z "" && echo 1 || echo 0
1
  1. []条件测试语法

如果文件存在,输出1,否则输出0

[ -f file ] && echo 1 || echo 0
  1. [[]]语法格式

如果文件存在,输出y,否则输出n

[[ -f file ]] && echo y || echo n

三种方法的比较
[[]] 与其他两种测试表达式的其别在于,其内可以使用通配符,并且&& || > <等操作符号可以应用与其内,但是不能用于[]中,在[]中一般使用 -a -o -gt(用于整数)、-lt等操作符

练习2 文件测表达式

测试shell变量

[root@zycentos7 ~]# file1=/etc/services
[root@zycentos7 ~]# [ -f "$file1" ] && echo y || echo n
y

练习3 字符串测试表达式

[ -n "abc" ] && echo 1 || echo 0

6.shell脚本之条件判断

if条件语句

单分支结构

if <条件表达式>
then
	指令
fi

双分支结构

if <条件表达式>
then
	指令集1
else
	指令集2
fi

多分支结构

if <条件表达式1>
then
	指令1
elif <条件表达式2>
then
	指令2
else
	指令3
fi

练习 猜一猜

while true
do
read -p "input the number you think:" num
if [ $num -gt 58 ]
then
    echo "猜大了"
elif [ $num -lt 58 ]
then
    echo "猜小了"
else
    echo "恭喜,猜对了"
    exit 10
fi
done

case条件语句

case "变量" in
	值1)
		指令1...
		;;
	值2)
		指令2...
		;;
	*)
		指令3...
esac

企业案例 rsync服务的启动脚本

case $1 in
    start)
        rsync --daemon
        #echo "start"
        ;;
    stop)
        pkill rsync
        #echo "stop"
        ;;
    restart)
        pkill rsync
        rsync --daemon
        #echo "restart"
        ;;
    *)
        echo "User:`basename $0` start | stop | restart."
esac
[root@zycentos7 ~]# mv RSYNC.sh /usr/local/bin/RSYNC
[root@zycentos7 ~]# chmod a+x /usr/local/bin/RSYNC
[root@zycentos7 ~]# ll /usr/local/bin/RSYNC
-rwxr-xr-x. 1 root root 441 Dec 15 04:38 /usr/local/bin/RSYNC
[root@zycentos7 ~]# RSYNC start

给输出的字符串加颜色

 echo -e "\033[32m 字符串 \033[0m"
033[#m中的#值 颜色
30
31
32 绿
33
34
35 洋红
36 蓝绿
37

给输出的字符串加背景颜色

 echo -e "\033[40;32m 字符串 \033[0m"
033[#;中的#值 颜色
40
41
42 绿
33
44
45 洋红
46 蓝绿
47

让输出的字符串闪烁

echo -e "\033[05;37m 字符串 \033[0m"

推荐使用 通过定义变量的方式给字体加颜色

RED_COLOR='\E[1;31m'
GREEN_COLOR='\E[1;32m'
YELLOW_COLOR='\E[1;33m'
BLUE_COLOR='\E[1;34m'
PINK='\E[1;35m'
RES='\E[0m'
echo -e "${RED_COLOR}======red color======${RES}"
echo -e "${YELLOW_COLOR}======yellow color======${RES}"
echo -e "${BLUE_COLOR}======blue color======${RES}"
echo -e "${GREEN_COLOR}======green color======${RES}"
echo -e "${PINK}======pink color======${RES}"

7.shell脚本之循环

while循环

while <条件表达式>
do
	指令...
done

util循环

util <条件表达式>
do
	指令...
done

for循环

for 变量名 in 变量取值列表
do
	指令...
done

c风格

for ((exp1;exp2;exp3))
do
	指令...
done

练习 修改for循环默认的分隔符(空格)为换行

IFS_old=$IFS
IFS=$'\n'
for i in `cat /etc/passwd`
do
    echo $i
done
IFS=$IFS_old

循环控制

条件与循环控制及程序返回值命令知识表

命令 说明
break n 省略n表示跳出整个循环,n表示跳出循环的层数
continue n 省略n表示跳过本次循环,忽略本次循环的剩余代码,进人循环的下一次循环,n表示退到第n层继续循环
exit n 退出当前Shell程序,n为上一次程序执行的状态返回值,n可以省略($?)
return n 用于在函数里作为函数的返回值,以判断函数执行是否正确($?)

练习 输出九九乘法表

for((i=1;i<=9;i++))
do
    for((j=1;j<=i;j++))
    do
        echo -ne "$i*$j=$(($i*$j))\t"
    done
    echo ""
done

7.shell脚本之数组

数组赋值

  1. 一次赋一个值(单独定义数组的各个部分) 数组名[下标]=变量值
[root@zycentos7 ~]# array[0]=pear
[root@zycentos7 ~]# array[1]=apple
[root@zycentos7 ~]# echo ${array} ${array[1]}
pear apple
  1. 一次赋多个值
passwd=(`cat /etc/passwd`)	;将该文件中的每一个行作为一个元数赋值给数组passwd
array6=(1 2 3 4 5 6 "linux shell" [20]=ansinble)	;一次赋多个值,并指定其中某些值
array=(yi bochen)	;数组以空格符分隔各个数组元素
array=("yi bochen")	;数组元素值中含有空格,加""
path=($PATH $SHELL)

数组操作

  1. 获取数组长度
echo ${#colors[@]}
echo ${#colors[*]}
  1. 获取某一数组元素长度
echo ${#colors[1]}
  1. 引用数组
${数组名[下标]}
${数组名[@]:1}	;从下标为1开始打印全部
${数组名[@]:1:2}	;从下标为1开始打印2个
  1. 遍历数组

直接输出数组所有元素

echo ${path[@]}

按数组元素取值,可以不连续取值

for var in ${colors[@]};do echo $var;done

按数组下标(索引)取值,只能连续取值

for((i=0;i<${#colors[@]};i++));do echo ${colors[i]};done
  1. 删除数组
unset array[2]	;删除数组中的某个元素
unset array	;删除整个数组
  1. 修改数组中的某一元素值
declare -a filename[1]=Linux
  1. 根据索引查看
echo ${filename[@]:1}	;从下标为1开始打印全部
echo ${filename[@]:1:2}	;从下标为1开始打印2个

练习1 标准输入保存到数组里

读入标准输入
(1) $1 $2
(2) read -p “” num
(3) cat << EOF

 EOF

i=0
while true
do
read -p "" num
array[$i]=$num
echo ${array[@]}
let i++
done

练习2 字符串保存到数组里
加空格保存

使用空格对字符进行分隔
str=`echo $str|sed ‘s/./& /g’`

chars="abcde"
chars=`echo $chars|sed 's/./& /g'`
j=0
for i in $chars
do
    array[$j]=$i
    let j++
    echo ${array[@]}
done

将字符逐个取出

chars="abcd"
for((n=0;n<${#chars};n++))
do
array[$n]=${chars:$n:1}
echo ${array[@]}
done

练习3 将数组里的数值分别乘以8并打印

array=(1 2 3 4 5)
j=0
for i in ${array[@]}
do
    array[$j]=$(($i*8))
    let j++
done
echo ${array[@]}

8.shell脚本之函数

函数是完成特定功能的代码片段(块)
在shell中定义函数可以使用代码模块化,便于复用代码,函数必须先定义才可以使用

定义函数

格式1

function 函数名( )
{
指令… }

格式2

函数名()
{
指令… }

格式3

function 函数名
{
指令… }

调用函数

函数名
函数名 参数1 参数2

练习1 写一个脚本,判定192.168.232.123-192.168.232.126之间的主机哪些在线

要求:
1、使用函数来实现一台主机的判定过程
2、在主程序中来调用此函数判定指定范围内的所有主机的在线情况

  1. 直接使用函数实现(无参数,无返回值)
PING(){
    for i in `seq 123 126`
    do
        if ping -c 1 -w 1 192.168.232.$i &>/dev/null 2>&1
        then
            echo "192.168.232.$i is Tong"
        else
            echo "192.168.232.$i is NOT Tong"
        fi
    done
}
PING
  1. 使用函数返回值判断(有参数,有返回值)
PING(){
    if ping -c 1 -W 1 $1 &> /dev/null
    then
        return 0
    else
        return 1
    fi
}
for i in {123..126}
do
    PING 192.168.232.$i
    if [ $? -eq 0 ]
        then
            echo "192.168.232.$i is up."
        else
            echo "192.168.232.$i is down."
    fi
done
  1. 使用函数传参(有参数,无返回值)
PING(){
for i in `seq $1 $2`
do
   ping -c 1 $3$i >/dev/null 2>&1
   if [ $? -eq 0 ]
   then
       echo "$3$i Tong.."
   else
       echo "$3$i Not Tong.."
   fi
done
}
PING $1 $2 "192.168.232."
[root@zycentos7 ~]# sh ping.sh 123 126
192.168.232.123 Not Tong..
192.168.232.124 Not Tong..
192.168.232.125 Tong..
192.168.232.126 Not Tong..

练习2 写一个脚本,使用函数完成

要求
1、函数能够接受一个参数,参数为用户名
判断一个用户是否存在,如果存在,就返回此用户的shell和UID,并返回正常状态值;如果不存在,就说此用户不存在,并返回错误状态值
2、在主程序中调用函数

USER_CHECK(){
    if id $1 >/dev/null 2>&1
    then
        grep "^$1:" /etc/passwd | cut -d: -f 3,7
        n=0
    else
        echo "no"
        n=1
    fi
    return $n
}
while true
do
read -p "username[q|Q,exit]:" username
if [[ $username == "" ]]
then
    echo "root"
elif [[ $username == "q" || $username == "Q" ]]
then
    exit
else
    USER_CHECK $username
    if [ $? -eq 0 ]
    then
        echo "状态值:0 true"
    else
        echo "状态值:1 false"
    fi
fi
done

9.脚本运行方法

用法 说明
sh while1.sh & 把脚本whilel .sh放到后台执行(在后台运行脚本时常用的方法)
ctl+c 停止执行当前脚本或任务
ctl+z 暂停执行当前脚本或任务
bg 把当前脚本或任务放到后台执行,background
fg 把当前脚本或任务放到前台执行,如果有多个任务,可以使用fg加任务编号调出对应的脚本任务,如fg2,是指调出第二个脚本任务,frontground
jobs 查看当前执行的脚本或任务
kill 关闭执行的脚本任务,即以“kill %任务编号”的形式关闭脚本,这个任务编号,可以通过jobs来获得
killall 或 pkill 杀掉进程
ps 查看进程
pstree 显示进程状态树
top 显示进程
renice 改变优先权
nohup 用户退出系统之后继续工作
pgrep 查找匹配条件的进程
strace 跟踪一个进程的系统调用情况
ltrace 跟踪进程调用库函数的情况

练习

练习1 用户批量管理工具(增删改查)

#定义变量
passwd="123456"
read_username="read -p "请输入用户名:" username"
read_num1="read -p "起始位置:" num1"
read_num2="read -p "终止位置:" num2"

#判断用户是否存在
CHECK_USER(){
    id $username$i >/dev/null 2>&1
    if [ $? -eq 0 ]
    then
        n=0
    else
        n=1
    fi
    return $n
}

#创建用户
CREATE_USER(){
    #read -p "请输入用户名" username
    #read -p "请输入用户名" num1
    #read -p "请输入用户名" num2
    useradd $username$i
    echo "$passwd" |passwd --stdin $username$i >/dev/null 2>&1
}

#实现完整的用户创建功能
CREATE_USER_FULL(){
    $read_username
    $read_num1
    $read_num2
    #read -p "请输入用户名" username
    #read -p "起始位置" num1
    #read -p "终止位置" num2
    for i in `seq -f "%02g" $num1 $num2`
    do
        CHECK_USER
        if [ $? -eq 1 ]
        then
            CREATE_USER
        else
            echo "$username$i 已存在"
        fi
    done
}

#实现函数查看功能
SCAN_USER_LIST(){
    array=`awk -F: '$3>=1000 {print $1}' /etc/passwd`
    echo ${array[@]}
}

#修改用户密码
UPDATE_PASSWD(){
    echo "$1" |passwd --stdin $username$i >/dev/null 2>&1
}

#实现批量修改用户密码
UPDATE_PASSWD_FULL(){
    $read_username
    $read_num1
    $read_num2
    #read -p "请输入用户名" username
    #read -p "起始位置" num1
    #read -p "终止位置" num2
    read -p "请输入密码:" passwd
    for i in `seq -f "%02g" $num1 $num2`
    do
        UPDATE_PASSWD $passwd
    done
}

#实现批量删除用户
DELETE_USER_FULL(){
    $read_username
    $read_num1
    $read_num2
    #read -p "请输入用户名" username
    #read -p "起始位置" num1
    #read -p "终止位置" num2
    for i in `seq -f "%02g" $num1 $num2`
    do
        userdel -r $username$i
    done
}

while true
do
cat << EOF
--------------------菜单----------------------
+           1.创建用户                       +
+           2.修改用户密码                   +
+           3.删除用户                       +
+           4.查看用户                       +
+           5.退出                           +
----------------------------------------------
EOF
read -p "请输入菜单号(1-5):" num
case $num in
    1)
        CREATE_USER_FULL
        ;;
    2)
        UPDATE_PASSWD_FULL
        ;;
    3)
        DELETE_USER_FULL
        ;;
    4)
        SCAN_USER_LIST
        ;;
    5)
        exit
        ;;
    *)
        echo "请输入正确的菜单号..."
esac
done

练习2 获取随机字符串或数字
(1)获取随机8位字符串
(2)获取随机8位数字

length=8
i=1
seq=(0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
num_seq=${#seq[@]}
while [ "$i" -le "$length" ]
do
     seqrand[$i]=${seq[$((RANDOM%num_seq))]}
     let "i=i+1"
done
echo "The random string is:"
for j in ${seqrand[@]}
do
    echo -n $j
done
[root@zycentos7 ~]# uptime|awk -F":|," '{print "1分钟平均负载:",$7"\n5分钟平均 负载:"$8"\n15分钟平均负载:"$9}'
1分钟平均负载:  0.00
5分钟平均负载: 0.01
15分钟平均负载: 0.05

你可能感兴趣的:(Linux基础)