阿汤博客-承接中小企业服务器维护和网站维护,有意者可以联系博主!

bash基础及shell脚本编程初步

学习笔记 988℃ 0评论

终端,附着在终端的接口程序

GUI:KDE,GNome ,Xfce

CLI : /etc/shells

bash

zsh

fish

bash的特性:

命令行展开: ~,{}

命令别名: alias, unalias

命令历史: history

文件名通配:glob

快捷键:Ctrl+a,e , u , k ,l

命令补全: $PATH

路径补全:

bash特性之:命令hash

缓存此前命令的查找结果:key-value

key:搜索键

value: 值

hash命令:

hash:列出

hash -d COMMAND:删除

hash -r :清空

bash的特性之:变量

程序:指令+数据

指令:由程序文件提供

数据:IO设备、文件、管道、变量

程序:算法+数据结构

变量名+指向的内存空间

变量赋值:name=value

变量类型:存储格式、表示数据范围、参与的运算

编程语言:

强类型变量:

弱类型变量:

bash把所有变量统统视作字符型; 

bash中的变量无需事先声明;相当于,把声明和复制过程同时实现

声明:类型,变量名

变量替换:把变量名出现的位置替换为其所指向的内存空间中的数据

变量引用: ${var_name} ,$var_name

变量名:变量名只能包含数字、字母和下划线,而且不能以数字开头,不能使用程序的保留字段:if while for then

变量:见名知义,命名机制遵循某种法则

bash变量类型:

本地变量:作用域仅为当前shell进程

环境变量:作用域仅为当前shell进程及其子进程

局部变量:作用域仅为代码片段(函数上下文)

位置参数变量:当执行脚本的shell进程传递的参数

$1,$2…

shift

特殊变量:shell内置的有特殊功能的变量

$?:上一个命令的执行状态返回值

0:成功

1-255:失败

$#:参数的个数

$*:参数列表

[email protected]: 参数列表

本地变量:

变量赋值:name=value

变量引用:${name}, $name

"":变量为会替换为其值

'':变量名不会替换为其值

查看变量:set

撤销变量:unset name

注意:此处非变量引用

环境变量:

变量赋值:

(1)export name=value

(2)name=value
         export name

(3)declare -x name=value

(4)name=value

declare -x name

变量引用:${name}, $name

注意:bash内嵌了许多环境变量(通常为全大写字符),用于定义bash的工作环境

PATH ,HISTFILE, HISTSIZE, HISTFILESIZE,HISTCONTROL, SHELL ,HOME ,UID ,PWD ,OLDPWD

查看环境变量:export ,declare -x, printenv ,env

撤销环境变量:unset name

只读变量:

(1)declare -r name

(2)readonly name

只读变量无法重新赋值,并且不支持撤销;存活时间为当前shell进程的生命周期,随shell进程终止而终止

bash特性之多命令执行:

COMMAND1; COMMAND2; COMMAND3; ….

逻辑运算:

真(true, yes, on ,1)

假(false, no , off , 0)

与:

1&&1=1

1&&0=0

0&&1=0

0&&0=0

或:

1 || 1=1

1 || 0=1

0 || 1=1

0 || 0=0

非:

!1=0

!0=1

短路法则:

COMMAND1 && COMMAND2

COMMAND1为“假”,则COMMAND2不会再执行

否则,COMMAND1为“真”,则COMMAND2必须执行

COMMAND1 || COMMAND2
COMMAND1为“真”,则COMMAND2不会再执行
否则,COMMAND1为“假”,则COMMAND2必须执行

例:id $username || useradd $useradd

练习1:

1、添加3个用户user1,user2,user3,先判断用户是否存在,不存在而后再添加

2、显示当前系统上共有多少个用户

#!/bin/bash
for i in user1 user2 user3
do
! id $i &> /dev/null  && useradd $i && echo $i | passwd –stdin $i &> /dev/null || echo "$i is exists"
done
sum=`wc -l /etc/passwd | cut -d' ' -f1`
echo "$sum /etc/passwd users"

shell脚本编程:

编程语言的分类:根据运行方式

编译运行:源代码–>编译器(编译)–>程序文件

解释运行:源代码–>运行时启动解释器,由解释器边解释边运行

根据其编程过程中功能的实现是调用库还是调用外部的程序文件:

shell脚本编程:

利用系统上的命令及编程组件进行编程

完整编程:

利用库和编程组件进行编程

编程模型:过程式编程语言,面向对象的编程语言

程序+指令+数据

过程式:以指令为中心来组织代码,数据是服务于代码;

顺序执行:

选择执行:

循环执行:

代表:c语言,bash

对象式:以数据为中心来组织代码,围绕数据来组织指令;

类(class):实例化对象,method:

代表:java,c++, python

shell脚本编程:过程式编程,解释运行,依赖于外部程序程序文件运行;

如何写shell脚本:

脚本文件的第一行,顶格:给出shebang, 解析器路径,用于指明解析执行当前脚本解析器程序文件

常见的解析器:

#!/bin/bash

#!/usr/bin/python

#!/usr/bin/perl

文本编辑器:nano

行编辑器:sed

全屏幕编程器:nano, vi , vim

shell脚本是什么?

命令的堆积:

但很多命令不具有幂等性,需要用程序逻辑来判断运行条件十分满足,以避免其运行中发生错误;

运行脚本:

(1)赋予执行权限,并直接运行此程序文件;

chmod +x /path/to/script_file

/path/to/script_file

(2)直接运行解析器,将脚本以命令行参数传递给解释器程序

bash/path/to/script_file

注意:脚本中的空白行会被解释器忽略

脚本中,除了shebang,余下所有#开头的行,都会被视作注释而被忽略;此即为注释行

shell脚本的运行是通过运行一个子shell进程实现的

练习1:写一个脚本,实现如下功能:

(1)显示/etc目录下所有以大写P或小写p开头的文件或目录本身

(2)显示/var目录下所有文件或者目录本身,并将显示结果中的小写字母转换为大写显示

(3)创建临时文件/tmp/myfile.XXXX

bash的配置文件:

两类:

profile类:为交互式登录的shell提供配置

bashrc类:为非交互式登录的shell提供配置

登录类型:

交互式登录shell进程:

直接通过某终端输入账号和密码后登录打开的shell进程;

使用su命令:su -username,或者使用su -l username执行的登录切换。

非交互式登录shell进程:

su username执行的登录切换;

图形界面下打开的终端

profile类:

全局:对所有用户都生效:

/etc/profile

/etc/profid.d/*.sh

用户个人:仅对当前用户有效;

~/.bash_profile

功用:

1、用户定义环境变量

2、运行命令或脚本

bashrc类:

全局:

/etc/bashrc

用户个人:

~/.bashrc

功用:

1、定义本地变量:

2、定义命令别名:

注意:仅管理员可修改全局配置文件

交互式登录shell进程:

/etc/profile–>/etc/profile.d/*–>/.bash_profile–>~/.bashrc–>/etc/bashrc

非交互式登录shell进程:

~/.bashrc–>/etc/bashrc–>/etc/profile.d/*

命令行中定义的特性,例如变量和别名作用域为当前shell进程的生命周期

配置文件定义的特性,只对随后新启动的shell进程有效;

让通过配置文件定义的特性立即生效:

(1)通过命令行中重复定义一次;

(2)让shell进程重读配置文件;

source /path/from/conf_file

. /path/from/conf_file

问题1:定义对所有用户都生效的命令别名,例如“lftps=lftp 172.16.1.110/pub”?

问题2:让centos用户登录时,提示其已经登录,并提示当前时间。

vim /etc/profile.d//welcome.sh

echo "Welcome login!"

echo -n "NOW Time is: " 

date

echo -n "Login ip:"

who | awk 'END{print $5}' |tr "()" " "

bash的算术运算

+ ,- ,*,/ ,** ,%

取模,取余%

3%2=1

100%55=45

算术运算格式

(1)let VAR=算术运算表达式

(2)VAR=$[算术运算表达式]

(3)VAR=$((算术运行表达式))

(4)VAR=$(expr $ARG1 $OP $ARG2)

注意:乘法符号在有些场景中需要使用转义符

练习:写一个脚本,完成如下功能;添加三个用户;求此三个用户的UID之和

#i/usr/bash

for((i=1;i<=3;i++));do

username="user$i"

useradd "$username"

echo "$username" | passwd –stdin $username

num=$(id -u "$username")

let sum=sum+$num

done

echo $sum

条件判断

bash中如何实现条件判断?

条件测试类型

整数测试

字符测试

文件测试

条件测试的表达式

[ expression ]

[[ expression ]]

test expression

整数比较:

-eq:测试两个整数是否相等:比如$a -eq $b

-ne:测试两个整数是否不等:不等,为真:相等,为假

-gt:测试一个数是否大于另一个数:大于,为真;否则,为假

-lt:测试一个数是否小于另一个数:小于,为真;否则,为假

-ge:大于或等于

-le:小于或等于

练习:

1.判断当前系统上是否有用户的默认shell为bash;如果有,就显示有多少个这类用户;否则,就显示没有这类用户

2.判定命令历史中历史命令的总条目是否大于1000;如果大于就显示“some command will gone”;否则显示“OK”

num=`history | tail -1 | awk  '{print $1}'`

if [[ $num -gt 1000 ]];then

echo "OK!"

else

echo "Some command will gone"

fi

3.给定一个用户,获取其mime警告期限;而判断用户mime使用期限是否已经小于警告期限(提示:计算方法,最长使用期限减去已经使用的天数即为剩余使用期限);如果小于,则显示“warning”;否则,就显示“OK”

#!/bin/bash

TODAY=`date +%s`

ZUIJIN=`tail -1 /etc/shadow | cut -d":" -f3`

SHIY ))

ZUICHANG=`tail -1 /etc/shadow | cut -d":" -f5`

JINGAO=`tail -1 /etc/shadow | cut -d":" -f6`

SY=$[ $ZUICHANG-$SHIYONG  ]

if [ $SY -gt $JINGAO ];then

echo "OK!"

else

echo "Warning!"

fi 

文件测试:

-e FILE:测试文件是否存在 存在为true

-f FILE:测试文件是否为普通文件

-d FILE:测试指定路径是否为目录

-r FILE:测试当前用户对指定文件是否有读权限

-w FILE:

-x FILE:

[ -x /etc/rc.d/rc.sysinit ]

字符串测试:

== 测试是否相等,等为真,不等为假

!=:测试是否不等,不等为真,等为假

>

<

-z sting:测试指定字符串是否为0,0为真,不为0则假

-n string: 测试指定字符串是否为0,不为0为真,0则假

-str string:测试指定字符串是否不空,不空为真,空则假

if []

组合测试:

-a:与关系

-o:或关系

!:非关系

if [ $# -gt 1 -a $# -le 3 ]

if [ $# -gt 1  ]  && [ $# -le 3 ]

测试脚本是否有语法错误:

bash -n

单步执行:

bash -x

练习

1.给定一个文件,如果是一个普通文件,就显示之;如果是一个目录,亦显示之;否则,此为无法识别之文件。

#!/bin/bash

FILE=/etc/shadow2

if [ ! -e $FILE ];then

        echo "NO such file."

        exit 1

fi

if [ -e $FILE ];then

        echo "wenjian" 

elif [ -d $FILE ];then

        echo "mulu"

else

        echo "Unknown"

fi

2.给脚本传递两个参数(整数);显示此两者之和,之积。

#/bin/bash

if [ $# -ne 2  ]; then

echo "Plese Enter Two AGE"

exit 2

fi

HE=$[ $1+$2 ]

JI=$[ $1*$2 ]

echo "$1 + $2=$HE"
echo "$1 * $2=$JI"

练习:

1、传递一个用户名参数脚本,判断此用户的用户名跟其基本组的组名是否一致,并将结果显示出来

#!/bin/bash

if  ! id $1 &>/dev/null;then

echo "NO such User"

exit 6

fi

G=`id -n -g $1`

if [ $G = $1 ];then

echo "yiyang"

else

echo " buyiyang"

fi

2、传递三个参数给脚本,第一个为整数,第二个为算术运算符,第三个为整数,将计算结果显示出来,要求保留两位精度。如: ./calc.sh 5 / 2

#!/bin/bash

echo "scale=2;$1$2$3 "|bc

3、传递三个参数给脚本,参数均为用户名。将此用户的账号信息提取出来后放置于/tmp/testusers.txt文件中,并要求每一行行首有行号。

#!/bin/bash

for i in [email protected];do

if  ! id $i &>/dev/null;then

echo "$i Not User"

exit 6

fi

done

echo "1 $1" | tee -a /tmp/testuser.txt

echo "2 $2" | tee -a /tmp/testuser.txt

echo "3 $3" | tee -a /tmp/testuser.txt

#!/bin/bash

for i in [email protected];do

if  ! id $i &>/dev/null;then

echo "$i Not User"

exit 6

fi

done

m=1

while (( $m<=$#  ));do

        for i in [email protected];do

        echo $m $i |tee -a /tmp/testusers.txt

        let "m++"

        done

done

4、写一个脚本:

判断当前主机的CPU生产商,其信息在/proc/cpuinfo文件中vendor id 一行中。

如果其生产商为AuthenticAMD,就显示为AMD公司

如果其生产商为GenuineIntel,就显示为Intel公司
否则,就说其为非主流公司

#!/bin/bash

CPU=`grep "vendor_id" /proc/cpuinfo | head -1 | awk '{print $3}'`

if [ $CPU == "AuthenticAMD" ];then

        echo "AMD"

elif [ $CPU == "GenuineIntel" ]

        echo "Intel"

else

        echo "Feizhuliu"

fi   

5、写一个脚本:

给脚本传递三个参数,判断其中 的最大数和最小数,并显示出来

#!/bin/bash

MAX=0

for i in [email protected];do

        if [ $i -gt $MAX ];then

                MAX=$i

        fi

done

        echo Max is $MAX

SMALL=0
NUM=0
for i in [email protected];do
        let NUM++
        [ $NUM -eq 1 ]&& SMALL=$i
        if [ $i -lt $SMALL ];then
                SMALL=$i
        fi
done
        echo Small is $SMALL

条件判断,控制结构:

单分支if语句

if 判断条件;then

statement1

statement1

fi

双分支的if语句:

if 判断条件;then

statement1

statement2

else

statement3

statement4

fi

多分支if语句:

if 判断条件;then

statement1

statement2

elif 判断条件2;then

statement3

statement4

elif 判断条件3;then

statement5
statement6

else

statement7

fi

case语句

case SWITCH in

value1)

statement

;;

value2)

statement

;;

*)

statement

;;

esac

写一个脚本:

只接收参数start,stop,restrart,status其中之一。

#!/bin/bash

case $1 in

'start')

        echo "start!";;

'stop')

        echo "stop!";;

'restart')

        echo "restart!";;

'staus')

        echo "staus!";;

*)

        echo `basename $0` "(start|stop|restrart|status)"

esac

写一个脚本,可以接受选项及参数,而后能获取每一个选项,及选项的参数;并能根据选项参数做出特定的操作,比如:

adminusers.sh –add tom,jerry   –del tom,blair  -v|–verbose -h|–help

#!/bin/bash

DEBUG=0

ADD=0

DEL=0

for i in `seq 0 $#`;do

if [ $# -gt 0 ];then

case $1 in

-v|–verbose)

        DEBUG=1

        shift ;;

-h|–help)

        echo "Usage: `basename $0` –add USER_LIST –del USER_LIST -v|–verbose -h|–help"

        exit 0 ;;

–add)

        ADD=1

        USERS=$2

        shift 2 ;;

–del)

        DEL=1

        USERS=$2

        shift 2 ;;

*)

       echo "Usage: `basename $0` –add USER_LIST –del USER_LIST -v|–verbose -h|–help"

       exit 6 ;;

esac

fi

done

if [ $ADD -eq 1 ];then

        for user in  `echo $USERS | tr "," " "`;do

                if id $user &>/dev/null;then

                         [ $DEBUG -eq 1 ] && echo "$user exists"

                else

                        useradd $user

                        echo $user |  passwd –stdin $user &>/dev/null

                        [ $DEBUG -eq 1 ] && echo "Add user:$user finished! "

                fi

        done

fi

if [ $DEL -eq 1 ];then

        for user in  `echo $USERS | tr "," " "`;do

                if id $user &>/dev/null;then

                         userdel -rf $user &>/dev/null

                         [ $DEBUG -eq 1 ] && echo "$user Delete finished!"

                else

                        [ $DEBUG -eq 1 ] && echo "$user Not exist. "

                fi

        done

fi

练习:写一个脚本showlogged.sh,其中语法格式为:

showlogged.sh -v -c -h|–help

其中,-h选项只能单独使用,用于显示帮助信息;-c选项时,显示当前系统上登录的所有用户数;如果同时使用了-v选项,则即显示同时登录的用户数,又显示登录的用户的相关信息;如:

logged users: 4

they are:

root     pts/0        2016-10-09 12:38 (171.221.140.180)
root     pts/2        2016-10-09 13:15 (171.221.140.180)
root     pts/3        2016-10-09 13:16 (171.221.140.180)

#!/bin/bash

declare -i DEBUG=0

declare -i USER=0

for i in `seq 1 $#`;do

if [ $# -gt 0 ];then

case $1 in

-v)

        DEBUG=1

        shift;;

-h|–help)

        echo "Usage:`basename $0` -v -c -h|–help"

        exit 0;;

-c)

        USER=1

        shift ;;

*)

        echo "Usage:`basename $0` -v -c -h|–help"

        exit 7;;

esac

fi

done

if [ $USER -eq 1  ];then

        num=`who | wc -l`

        echo "Logged users:$num"

        if [ $DEBUG -eq 1 ];then

                echo "They are:" 

                who

        fi

fi

循环:进入条件,退出条件 

for循环格式:

for varaible in list ;do

循环体

done

for ((expr1;expr2;expr3));do

循环体

done

如何生成列表:

{1..100}

`seq [起始数 [步进长度] ] 结束数`

声明变量 declare -i sum=0

integer

-x 环境变量

#!/bin/bash

sum=0

for i in {1..100};do

        let sum=$sum+$i

done

echo $sum

练习:写一个脚本

(1、设定变量FILE的值为/etc/passwd

(2、依次向/etc/passwd中的每个用户问好,并显示对方的shell,如:Hello, root ,your shell: /bin/bash

(3、统计一共有多少个用户

#!/bin/bash

LIST=`awk -F: '{print $1}' /etc/passwd`

for i in $LIST;do

        BS=`grep "^$i\>" /etc/passwd | awk -F: '{print $7}'`

        echo "Hello,$i  Your bash is $BS"

done

sum=`wc -l /etc/passwd | cut -d' ' -f1`
echo "$sum /etc/passwd users"

2、写一个脚本

添加10个用户user1到user10,但要求只有用户不存在的情况下才添加

#!/bin/bash

for m in {1..10};do

i=user$m

! id $i &> /dev/null  && useradd $i && echo $i | passwd –stdin $i &> /dev/null || echo "$i is exists"

done

扩展:

接收一个参数:

add:添加user1-user10

del:删除user1-user10

其他:退出

#!/bin/bash
if [[ $1 != add && $1 != del ]];then
        echo "Plese ENter add OR del"
        echo "exit…"
        exit 6
fi
for i in {1..10};do
        NAME=user$i
        if  [[ $1 == add  ]];then
                useradd $NAME &>/dev/null && echo $NAME | passwd –stdin $NAME&>/dev/null && echo "$NAME add OK!" || echo "$NAME is Exists"
        elif [[ $1 == del  ]];then
                userdel -rf $NAME &>/dev/null&& echo "$NAME delete OK!" || echo "$NAME no Such"
        else
                echo "ARG Error!"       
        fi
done

3、写一个脚本:

计算100以内所有能被3整除的正整数的和

#!/bin/bash
declare -i sum=0
for i in {1..100};do
        if (($i%3==0));then
                let sum=$[$sum+$i]
        fi
done
        echo $sum 

4、写一个脚本:

计算100以内所有奇数的和以及所有偶数的和

#!/bin/bash

for i in {1..100};do

if (($i%2==0));then

let sum=$sum+$i

fi

done

echo $sum

#!/bin/bash
for i in {1..100};do
if (($i%2==1));then
let sum=$sum+$i
fi
done
echo $sum

5、写一个脚本,分别显示当前系统上所有默认shell为bash的用户和默认shell为/sbin/nologin的用户,并统计各类shell下的用户总数。显示结果如:

bash,3users,they are: root,redhat,gentoo

nologin,2users ,they are: bin ftp

#!/bin/bash

BASH_array=(`grep  "\<bash\>$" /etc/passwd | cut -d":" -f1`)

BASH_num=`grep -c "\<bash\>$" /etc/passwd`

NOLOGIN_array=(`grep  "\<nologin\>$" /etc/passwd | cut -d":" -f1`)

NOlOGIN_num=`grep -c "\<nologin\>$" /etc/passwd`

echo "bash,${BASH_num} users, they are:${BASH_array[@]}"

echo "nologin,${NOlOGIN_num} users, they are:${NOLOGIN_array[@]}"

while循环:

适用于循环次数未知的场景,要有条件退出

while CONDITION;do

循环体

循环控制变量修正表达式

done

进入条件:CONDITION测试为“真”

退出条件:CONDITION测试为“假”

while的特殊用法一:

while : ;do

done

while的特殊用法二:

while read LINE;do

done < /PATH/TO/SOMEFILE

练习:计算100以内所有正整数的和。

#!/bin/bash

declare -i i=1

declare -i num=0

while [ $i -le 100  ];do

let sum=$sum+$i

let i++

done

echo $sum

写一个脚本

1.显示一个菜单给用户:

d|D) show disk usages.

m|M) show memory usages

s|S) show swap usages

*) quit

2.当用户给定选项后显示相应的内容

扩展:当用户选择完成,显示相应的信息后,不退出;而让用户再一次选择,再次显示相应内容;除了用户使用quit

#!/bin/bash

cat << EOF

d|D) show disk usages.

m|M) show memory usages.

s|S) show swap usages.

*) quit.

EOF

read -p"Please Choice:" CHOICE

while [ $CHOICE != 'quit' ];do

        case $CHOICE in

        d|D)

                echo -e "\033[32mdisk usage: \033[0m"

                df -Ph;;

        m|M)

                echo -e "\033[33mMemory usage: \033[0m"

                free -m |grep "Mem";;

        s|S)

                echo -e "\033[34mSwap usage: \033[0m"

                free -m |grep "Swap";;

        *)

                echo -e "\033[31mUnknown! \033[0m"

        esac

        read -p"Please Choice:" CHOICE

done

until循环:
until CONDITION;do

循环体
循环控制变量修正表达式

done

进入条件:CONDITION测试为“假”
退出条件:CONDITION测试为“真”

exit:退出循环

如果脚本没有明确定义退出状态码,那么最后执行的一条命令的退出码即为脚本的退出状态码

break:提前退出循环

continue:提前结束本轮循环,而进入下一轮循环

示例:求100以内正整数的和

练习:分别使用for,while,until实现

1、分别求100以内所有偶数之和,100以内所有奇数之和;

#!/bin/bash

for m in {1..100}

do

        if(($m%2==0));then

         let num=$num+$m

        fi

done

        echo $num

#!/bin/bash

for m in {1..100}

do

        if(($m%2==1));then

         let num=$num+$m

        fi

done

        echo $num

2、创建10个用户,user101-user110;密码同用户名

#!/bin/bash
for m in {101..110};do
i=user$m
! id $i &> /dev/null  && useradd $i && echo $i | passwd –stdin $i &> /dev/null || echo "$i is exists"
done

3、打印99乘法表;

#!/bin/bash
for m in {1..9}
do
        for((n=1;n<=m;n++))
        do
                echo -n "$n*$m = $(($m*$n))   " 
        done
        echo -e "\n"
done

4、打印逆序的九九乘法表

#!/bin/bash
for m in {9..1}
do
        for((n=1;n<=m;n++))
        do
                echo -n "$n*$m = $(($m*$n))   " 
        done
        echo -e "\n"
done

5、写一个脚本,利用RANDOM生成10个随机数,并找出其中最大值,和最小值。

#!/bin/bash

#

declare -i max=0

declare -i min=0

for i in {1..10};do

        rand=$RANDOM

        [ $i -eq 1 ] && min=$rand

        if [ $i -le 9 ];then

                echo -n "$rand,"

        else

                echo $rand

        fi

        [ $rand -gt $max  ] && max=$rand

        [ $rand -lt $min ] && min=$rand

done

        echo "MAX:$max"
        echo "MIN:$min"

写一个脚本:

1、通过ping命令测试192.168.0.151到192.168.0.254之间的所有主机是否在线,如果在线,就显示“ip is up”,其中的IP要换为真正的IP地址,且以绿色显示;如果不在线,就显示“ip is  down ”,其中的IP地址换为真正的IP地址,且以红色显示。

要求:分别饰演while,until ,for(两种形式)循环实现。

#!/bin/bash

declare -i LAST=150

while [ $LAST -le 254starry ];do

#until [ $LAST -gt 254 ];do

IP=222.73.22.$LAST

if ping -c 1 -W 1 $IP &> /dev/null;then

echo -e "IP:\033[32m $IP \033[0m is up" 

else 

echo -e "IP:\033[31m $IP \033[0m is down" 

fi

let LAST++

done

#!/bin/bash
for i in {150..254};do
#for ((i=150;i<=254;i++));do
IP=222.73.22.$i
if ping -c 1 -W 1 $IP &> /dev/null;then
echo -e "IP:\033[32m $IP \033[0m is up" 
else 
echo -e "IP:\033[31m $IP \033[0m is down" 
fi
done

写一个脚本(前提:请为虚拟机新增一块硬盘,架设它为/dev/sdb),为指定的硬盘创建分区,

1)列出当前系统上所有的磁盘,让用户选择,如果选择quit则退出脚本;如果用户选择错误,就让用户重新选择

2)当用户选择后,提醒用户确认接下来的操作可能会损坏数据,并请用户确认,如果用户选择y就继续,n就退出,否则,让用户重新选择

3)抹除那块硬盘上的所有分区(提示:抹除所有分区后执行sync命令,并让脚本睡眠3秒种后再分区);并为其创建三个主分区,第一个为20M,第二个为512M,第三个为128M,且第三个为swap分区类型:(提示:将分区命令通过echo传送给fdisk即可实现)

#!/bin/bash

echo "Initial a Disk…"

echo -e "\033[31mWarning:Data will be lost!!!\033[0m"

fdisk -l | grep "Disk /dev/[vhs]d[a-z]" | awk -F: '{split($1,D," ");printf"Disk%d:%s\n",NR,D[2]}'

echo -en "\033[31mYour Choice:\033[0m"

read dk

if [ $dk == 'quit' ];then

        echo "Exiting…"

        exit 5

fi

until fdisk -l | grep "Disk /dev/[vhs]d[a-z]" | awk -F: '{print $1}' | grep "Disk $dk$" &>/dev/null ;do

echo -en "\033[31mChoose wrong, please choose again:\033[0m"

read dk

if [ $dk == 'quit' ];then

        echo "Exiting…"

        exit 5

fi

done

echo -en "\033[31mClear all data,Please Choice y or n:\033[0m"

read choice

until [ $choice == 'y' -o $choice == 'n'  ];do

echo -en "\033[31mChoose wrong, please choose y or n:\033[0m"

read choice

done

if [ $choice == 'n' ];then

echo "Quiting…"

exit 6

else

for i in `df -lh | grep "/dev/sdc" | awk '{print $1}'`;do

fuser -km $i

umount $i &>/dev/null

echo -e "\033[31m$i umount finish!\033[0m"

done

dd if=/dev/zero of=$dk bs=512 count=1 &>/dev/null

sync 

sleep 3

echo "

n

p

1

+20M

n

p

2

+512M

n

p

3

+128M

t

3

82

w" | fdisk $dk &>/dev/null

partprobe $dk  &>/dev/null

sync

sleep 2 

mke2fs -j ${dk}1 &>/dev/null

echo -e "\033[32m${dk}1 formatting finish!\033[0m"

mke2fs -j ${dk}2 &>/dev/null

echo -e "\033[32m${dk}2 formatting finish!\033[0m"

mkswap  ${dk}3 &>/dev/null

echo -e "\033[32m${dk}3 formatting finish!\033[0m"

fi

fdisk -l $dk

swapon ${dk}3

写一个脚本,完成以下功能:(说明:此脚本能于同一个repo文件中创建多个yum源的指向)

①接受一个文件名做为参数,此文件存放至/etc/yum.repos.d目录中,且文件名以.repo为后缀

②在脚本中,提醒用户输入repo id ;如果为quit,则退出脚本;否则,继续完成下面的步骤:

③repo name以及baseurl的路径,而后以repo文件的格式将其保存至指定的文件中;

④enabled默认为1,而gpgcheck默认设定为0

⑤此脚本会循环执行多次,除非用户为repo id指定为quit

#!/bin/bash

REPO=/etc/yum.repos.d/$1

if [ -e $REPO ];then

echo "The  $1 Exist!"

exit 3

fi

echo -en "\033[32mPlease Enter Repo Id:\033[0m"

read REPOID

until [ $REPOID == "quit" ];do

echo  "[$REPOID]" >> $REPO

echo -en "\033[34mPlease Enter Repo Name:\033[0m"

read REPONAME

echo "name=$REPONAME" >> $REPO

echo -en "\033[34mPlease Enter baseurl:\033[0m"

read BASEURL

echo "baseurl=$BASEURL" >> $REPO

echo -e "enabled=1\ngpgcheck=0" >>$REPO

echo -en "\033[32mPlease Enter Repo Id:\033[0m"

read REPOID

done

echo "quit…"

exit 0

写一个脚本,完成如下功能:(说明:此脚本能够为指定网卡创建别名,则指定地址;使用格式如;mkethalias.sh -v|–verbose -i ethx)

①-i 选项用于指定网卡;指定完成以后,要判断其是否存在,如果不存在,就退出

②如果用户知道的网卡存在,则让用户为其指定一个别名,此别名可以为空,如果不空,请确保其事先不存在,否则,要报错,并让用户重新输入

③在用户输入了一个正确的别名后,请用户输入地址和掩码;并将其配置在知道的别名上;

④如果用户使用了-v选选,则在配置完成后,显示其配置结果信息:否则,将显示

写一个脚本:

①判断一个值得的bash脚本是否有语法错误:如果有错误,则提醒用户键入Q或者q无视错误并退出,其他任何键可以通过vim打开这个值得的脚本

②如果用户通过vim打开编辑后保存并退出时任然有错误,则重复第一步中的内容:否则,就正常关闭退出

#!/bin/bash
until `bash -n $1 &>/dev/null`;do
echo -ne "\033[31mSyntax error ,[Q|q]  to quit bash; others for vim $1: \033[0m"
read INPUT
if [ $INPUT != "q" -a  $INPUT != "Q" ];then
vim $1
continue
else
echo "quit…!"
exit 5
fi
done
echo "$1 bash is OK!"

#!/bin/bash

until `bash -n $1 &>/dev/null`;do

echo -ne "\033[31mSyntax error ,[Q|q]  to quit bash; others for vim $1: \033[0m"

read CHOSE

case $CHOSE in

Q|q)

echo "Syntax error ,quit!"

exit 3;;

*)

vim $1;;

esac

done

echo "$1 bash is OK!"

函数

function:功能

结构化编程,不能独立运行,需要调用时执行,可以被多次调用

定义一个函数:

function FUNCNAME{

command

}

FUNCNAME(){

command

}

自定义执行状态返回值:

return #

0-255

写一个脚本,判定192.168.0.200-192.168.0.254之间的主机哪些在线。要求:

①使用函数来实现一台主机的判断过程

②在主程序中来调用此函数判定指定范围内的所有主机的在线情况

#!/bin/bash
PING(){
if ping -c 1 -W 1 $1 &>/dev/null;then

return 0

else

return 1

fi
}
for i in {200.254};do
IP=192.168.0.$i
PING $IP
if [ $? -eq 0 ];then

echo -e "IP:\033[32m $IP \033[0m is up" 

else
         echo -e "IP:\033[31m $IP \033[0m is down" 

        fi

done

写一个脚本:使用函数完成

①函数能够接受一个参数,参数为用户名;判断一个用户存在,如果存在就返回此用户的shell和UID;并返回正常状态值;如果不存在就说此用户不存在,并返回错误状态值

②在主程序中调用函数

扩展①:在主程序中,让用户自己输入用户名后,传递函数来进行判断;

扩展②:在主程序中,输入用户名判断后不退出脚本,而是提示用户继续输入下一个用户名;如果用户输入的用户不存在,请用户重新输入,但如果用户输入的是q或Q就退出。

#!/bin/bash
USERID(){
if id $1&>/dev/null;then

return 0

else

 return 1

fi
}
echo -ne "\033[32mPlease input UserName:\033[0m"
read USERNAME
while :;do

USERID $USERNAME

RETU=$?

if [ $USERNAME == "Q" -o $USERNAME == "q" ];then

echo -e "\033[35mquit…\033[0m"

exit 6

elif [ $RETU -eq 1 ];then

echo -ne "\033[31mUsers not exits!Please agin input:\033[0m"

read USERNAME

continue

else

cat /etc/passwd | grep "$USERNAME" | awk -F: '{printf "%s Uid:%d shell:%s\n",$1,$3,$NF}'

fi

echo -ne "\033[32mPlease input UserName:\033[0m"

read USERNAME

done

${parameter#*/word}
${parameter##*/word}

FILE=/usr/local/src

${FILE#*/}: usr/local/src

${FILE##*/}: src

${parameter%/*word}
${parameter%%/*word}

${FILE%/*}: /usr/local

${FILE%%/*}: 

服务样例脚本:

#!/bin/bash

#

# chkconfig:2345 45 55

# description:This is my services!

FILE=/var/lock/subsys/mylinux

NAME=`basename $0`

STATUS(){

if [  -e $FILE ];then

  echo -e "\033[32m$NAME is running… \033[0m"

else

  echo -e "\033[31m$NAME is stoped\033[0m"

fi 

}

USAGE(){

  echo "/etc/init.d/$NAME {start|stop|restart|status}"

}

case $1 in

start)

  echo "starting…"

  touch $FILE;;

stop)

  echo "stoping…"

  rm -f $FILE &>/dev/null;;

restart)

  echo "restarting…";;

status)

  STATUS ;;

*)

  USAGE;;
esac

复制命令以及命令依赖的库文件到新的根目录

#!/bin/bash

MYROOT=/www/myroot

cplib(){

  libdir=${1%/*}

  newlib=$MYROOT$libdir

  [ ! -d $newlib ] && mkdir -p $newlib

  [ ! -e $MYROOT$1 ] && cp $1 $newlib && echo -e "\033[35m$1 copy finished!\033[0m"

}

cpcmd(){

  cmddir=${1%/*}

  newcmd=$MYROOT$cmddir

  [ ! -d $newcmd  ]  && mkdir -p $newcmd

  [ ! -e $MYROOT$1 ] && cp $1 $newcmd

  for i in  `ldd $1 | grep -o "/.*lib\(64\)\?/[^[:space:]]\+"`;do

    cplib $i

  done

}

echo -ne "\033[34mPlease input Command:\033[0m"

read CMD

until [ $CMD == 'q' ];do

  ! which $CMD &>/dev/null && echo -ne "\033[31mCommand not exist,Please againe input:\033[0m" && read CMD && continue

  COMMAND=`which $CMD | grep -v "alias" | grep  -o "[^[:space:]]\+"`

  cpcmd $COMMAND

  echo -e "\033[32m$COMMAND copy finished!\033[0m"

  echo -ne "\033[33mPlease input Command:\033[0m"

  read CMD
done

变量中的字符长度:${#var}

系统函数库样本:

SCREEN=`stty -F /dev/console size 2>/dev/null`

COLUMNS=${SCREEN#* }

[ -z $COLUMNS ]&& COLUMNS=80

SPA_COL=$[$COLUMNS-14]

RED='\033[1;31m'

GREEN='\033[1;32m'

BLUE='\033[1;34m'

NORMAL='\033[0m'

success(){

  string=$1

  RT_SPA=$[$SPA_COL-${#string}]

  echo -n $string

  for i in `seq 1 $RT_SPA`;do

    echo -n " "

  done

    echo -e "[   ${GREEN}OK${NORMAL}   ]"

}

failde(){

  string=$1

  RT_SPA=$[$SPA_COL-${#string}]

  echo -n $string

  for i in `seq 1 $RT_SPA`;do

    echo -n " "

  done

    echo -e "[ ${RED}FAILED${NORMAL} ]"

}

变量赋值:

${parameter:-word}:如果parameter为空或未定义,则变量展开为“word”;否则,展开为parameter的值

${parameter:+word}:如果parameter为空或未定义,不做任何操作”;否则,展开为“word”的值

${parameter:=word}:如果parameter为空或未定义,则变量展开为“word”;并将展开后的值赋值给parameter

${parameter:offset}

${parameter:offset:length}:取子串,从offset处的后一个字符开始,取lenth长的子串

脚本配置文件:

/etc/rc.d/init.d服务脚本

服务脚本支持配置文件:/etc/sysconfig/服务脚本同名的配置文件

局部变量:

local VAR_NAME=

a=1

test (){

local a=$[3+4]

}

test

for i in 'seq $i 10';do

    echo $i

done

mktemp命令:

mktemp – create a temporary file or directory
mktemp [OPTION]… [TEMPLATE]

常用选项:

-d :创建临时目录

注意:mktemp会将创建的临时文件名直接返回,因此,可以直接通过命令引用保存起来

信号:

kill -SIGNAL PID

1:HUP

2:INT(Ctrl+c)

9:KILL

15:TERM

脚本中,能实现信号捕捉,但9和1无法捕捉

trap命令:信号捕捉

trap 'COMMAND' 信号类别

trap 'echo "no quit"' INT  Ctrl+c的时候输出 no quit

一个执行多个语句,语句间用分号分隔

#!/bin/bash
TRAP(){
  rm -rf /var/tmp/test
  echo "quit and cleaning"
}
trap 'TRAP;exit 5' INT
mkdir -p /var/tmp/test
while true;do
   file=/var/tmp/test/file-`date +%H%M%S`   
   touch $file
   echo "Touch $file ok!"
   sleep 2
done

数组

如何声明一个数组:

declare -a AA

赋值方法1:

AA[0]=jerry

AA[1]=tom

AA[2]=wendy

AA[6]=natasha

赋值方法2:

AA=(jerry  tom wendy natasha)

AA=([0]=jerry [1]=tom [2]=wendy [6]=natasha)

AA[3] =selina

AA[4] =nikita

${#AA[0]}:第一个元素的长度${#AA[1]}第二个元素个数的长度

${#AA[*]}: 元素个数

${#AA[@]}:元素个数

${AA[*]}: 所有元素
${AA[@]}:所有元素

练习:

1、数组的元素个数为1-39

2、数组元素不能相同

3、显示此数组各元素的值

#!/bin/bash

read -p "input numbers[1-39]:" NUM

declare -a ARRAY

BJ(){

   for j in `seq 0 $[${#ARRAY[@]}-1]`;do

      if [ $1 -eq ${ARRAY[$j]} ];then

        return 1

      fi

   done

   return 0

}

for i in `seq 0 $[$NUM-1]`;do

    while true;do

      RAND=$[$RANDOM%40]

      BJ $RAND

      if [ $? -eq 0 ];then

        break 

      fi

    done

      ARRAY[$i]=$RAND

      echo "${ARRAY[$i]}"

done

getopts命令:获取选项

$OPTARG 获取选项参数

$OPTIND 选项和参数的数量

bash脚本使用选项

#!/bin/bash

while getopts ":d:" SWITCH;do

   case $SWITCH in

     d)

        DESC=$OPTARG;;

    \?)

        echo "Usage:`basename $0` [-d descriprion] FILENAME";;

   esac

done

shift $[$OPTIND-1]

if ! grep "[^[:space:]]" $1 &> /dev/null;then

cat > $1 << EOF

#!/bin/bash

# Name:`basename $1`

# Description:$DESC

# Author:Tcj

# Version:0.0.1

# Datetime: `date "+%F %T"`

# Usage:`basename $1`

#

EOF

fi

vim + $1

until bash -n $1 &> /dev/null;do

    echo -ne "\033[31mSyntax error ,[Q|q]  to quit `basename $0`; others for vim $1: \033[0m"

    read CHOSE

    case $CHOSE in

 Q|q)

    echo "Syntax error ,quit!"

    exit 3;;

 *)

    vim $1;;

    esac

done

    echo "$1 bash grammar is OK!"

    chmod +x $1

练习:写一个脚本getinterface.sh,脚本可以接受选项(i,I,a),完成以下任务

1、使用以下形式:getinterface.sh [-i interface|-I IP|-a]

2、当用户使用-i选项时,显示其指定网卡的IP地址

3、当用户使用-I选项时,显示其后面的IP地址所属的网络接口

4、当用户单独使用-a选项时,显示所有网络接口及其IP地址(除lo除外)

#!/bin/bash

# Name:getinterface.sh

# Description: Get ehternet information

# Author:Tcj

# Version:0.0.1

# Datetime: 2016-11-28 23:12:23

# Usage:getinterface.sh [-i INTERFACE|-I IP|-a ]

#

GETIP(){

  if ! ifconfig | grep -o "^[^[:space:]]\+" | grep $1  &>/dev/null;then

     echo "Wrong ehtercard!"

     exit 5

  fi

  IP=`ifconfig $1 | grep -o "inet addr:[0-9\.]\+" | cut -d":" -f2`

}

GETNET(){

  if ! ifconfig  | grep -o "inet addr:[0-9\.]\+" | cut -d":" -f2 | grep $1 &>/dev/null;then

     echo "Wrong IP!"

     exit 6

  fi

  NETWORK=`ifconfig | grep -B 1 "inet addr:$1" | grep -o "^[^[:space:]]\+"`

}

GETALL(){

  for i in `ifconfig | grep -o "^[^[:space:]]\+" | grep -v "lo"`;do

     GETIP $i

     echo "$i:$IP"

  done

}

USAGE(){

  echo "usage:`basename $0`[-i INTERFACE|-I IP|-a ]"

}

while getopts ":i:I:a" OPT;do

   case $OPT in

      i)

 GETIP $OPTARG

 echo -e "$OPTARG:\033[32;1m$IP\033[0m";;

      I)

         GETNET $OPTARG

 echo -e "$OPTARG:\033[32;1m$NETWORK\033[0m";;

      a)

         GETALL;;

      \?)

         USAGE;;

   esac

done

转载请注明:阿汤博客 » bash基础及shell脚本编程初步

喜欢 (1)or分享 (0)