8-shell脚本基础

本章内容

◆ 编程基础
◆ 脚本基本格式
◆ 变量
◆ 运算
◆ 条件测试
◆ 条件判断if
◆ 条件判断case
◆ 配置用户环境

编程基础

● Linus:Talk is cheap, show me the code  
● 程序  
    程序:算法+数据结构  
    数据:是程序的核心  
    数据结构:数据在计算机中的类型和组织方式  
    算法:处理数据的方式  
● 程序编程风格:  
    过程式:以指令为中心,数据服务于指令  
    对象式:以数据为中心,指令服务于数据  
● shell程序:提供了编程能力,解释执行  

程序的执行方式

● 计算机:运行二进制指令  
● 编程语言:人与计算机之间交互的语言  
● 低级编程语言:  
    机器:二进制的0和1的序列,称为机器指令。与自然语言差异太大,难懂、难写  
    汇编:用一些助记符号替代机器指令,称为汇编语言  
        如:ADD A,B 将寄存器A的数与寄存器B的数相加得到的数放到寄存器A中  
        汇编语言写好的程序需要汇编程序转换成机器指令  
        汇编语言稍微好理解,即机器指令对应的助记符,助记符更接近自然语言  
● 高级编程语言:  
    编译:高级语言-->编译器-->机器代码-->执行  
        C,C++  
    解释:高级语言-->执行-->解释器-->机器代码  
        shell,python,php,JavaScript,perl  

编程基本概念

● 编程逻辑处理方式:  
    顺序执行  
    循环执行  
    选择执行  
● shell编程:过程式、解释执行  
    编程语言的基本结构:  
        各种系统命令的组合  
        数据存储:变量、数组  
        表达式:a + b  
        语句:if  

shell脚本基础

● shell脚本:  
    包含一些命令或声明,并符合一定格式的文本文件  
● 格式要求:首行shebang机制  
    #!/bin/bash  
    #!/usr/bin/python  
    #!/usr/bin/perl  
● shell脚本的用途有:  
    自动化常用命令  
    执行系统管理和故障排除  
    创建简单的应用程序  
    处理文本或文件  

创建shell脚本

● 第一步:使用文本编辑器来创建文本文件  
    第一行必须包括shell声明序列:#!  
        示例:#!/bin/bash  
    添加注释  
        注释以#开头  
● 第二步:运行脚本  
    给予执行权限,在命令行上指定脚本的绝对或相对路径  
    直接运行解释器,将脚本作为解释器程序的参数运行  

脚本规范

● 脚本代码开头约定  
    1、第一行一般为调用使用的语言  
    2、程序名,避免更改文件名为无法找到正确的文件  
    3、版本号  
    4、更改后的时间  
    5、作者相关信息  
    6、该程序的作用,及注意事项  
    7、最后是各版本的更新简要说明  

脚本的基本结构

● 脚本的基本结构  
    #!SHEBANG  
    CONFIGURATION_VARIABLES  
    FUNCTION_DEFINITIONS  
    MAIN_CODE  

#家目录下添加.vimrc文件

set tabstop=4       #table键4个空格  
set ignorecase      #忽略大小写  
set cursorline      #光标带下划线  
set autoindent      #自动对齐  
autocmd BufNewFile *.sh exec ":call SetTitle()"  
func SetTitle()  
    if expand("%:e") == 'sh'  
    call setline(1,"#!/bin/bash")   
    call setline(2,"#")   
    call setline(3,"#********************************************************************")   
    call setline(4,"#Author:        LiangJC")   
    call setline(5,"#QQ:            184116857")   
    call setline(6,"#Date:          ".strftime("%Y-%m-%d"))  
    call setline(7,"#FileName:      ".expand("%"))  
    call setline(8,"#URL:           http://www.magedu.com")  
    call setline(9,"#Description:       The test script")   
    call setline(10,"#Copyright (C):    ".strftime("%Y")." All rights reserved")  
    call setline(11,"#********************************************************************")   
    call setline(12,"")   
    endif  
endfunc  
autocmd BufNewFile * normal G  

shell脚本示例

#!/bin/bash  
# ------------------------------------------  
# Filename: hello.sh  
# Revision: 1.1  
# Date: 2017/06/01  
# Author: wang  
# Email: wang@gmail.com  
# Website: www.magedu.com  
# Description: This is the first script  
# Copyright: 2017 wang  
# License: GPL  
# ------------------------------------------  
echo "hello world"  

脚本调试

● 检测脚本中的语法错误  
    bash -n /path/to/some_script  
● 调试执行  
    bash -x /path/to/some_script  

变量

● 变量:命名的内存空间  
● 变量:变量类型  
    作用:  
        1、数据存储方式  
        2、参与的运算  
        3、表示的数据范围  
    类型:  
        字符  
        数值:整型、浮点型  

● 静态编译语言:使用变量前,先声明变量类型,之后类型不能改变,在编译时检查,如:java,c  
● 动态编译语言:不用事先声明,可随时改变类型,如bash,Python  
● 强类型语言:不同类型数据操作,必须经过强制转换才同一类型才能运算,如java , c# ,python  
    如:以下python代码  
    print(‘magedu’+ 10) 提示出错,不会自动转换类型  
    print(‘magedu’+str(10)) 结果为magedu10,需要显示转换类型  
● 弱类型语言:语言的运行时会隐式做数据类型转换。无须指定类型,默认均为字符型;参与运算会自动进行隐式类型转换;变量无须事先定义可直接调用  
    如:bash 不支持浮点数,php,javascript  
    
● Shell中变量命名法则:  
    1、不能使程序中的保留字:例如if, for  
    2、只能使用数字、字母及下划线,且不能以数字开头  
    3、见名知义  
    4、统一命名规则:驼峰命名法  
● Shell中命名建议规则:  
    1、变量名大写  
    2、局部变量小写  
    3、函数名小写  
    4、用英文名字,并体现出实际作用  

bash中变量的种类

● 根据变量的生效范围等标准划分下面变量类型  
    局部变量:生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子shell进程均无效  
    环境变量:生效范围为当前shell进程及其子进程  
    本地变量:生效范围为当前shell进程中某代码片断,通常指函数  
    位置变量:$1, $2, ...来表示,用于让脚本在脚本代码中调用通过命令行传递给它的参数  
    特殊变量:$?, $0, $*, $@, $#,$$  

局部变量

变量赋值:name=‘value’  
可以使用引用value  
    (1) 可以是直接字串:name=“root"  
    (2) 变量引用:name="$USER"  
    (3) 命令引用:name=`COMMAND`  
        name=$(COMMAND)  
变量引用:${name} 或者 $name  
    " " 弱引用,其中的变量引用会被替换为变量值  
    ' ' 强引用,其中的变量引用不会被替换为变量值,而保持原字符串  
显示已定义的所有变量:set  
删除变量:unset name  

#创建用户并下次登陆需要修改密码

#!/bin/bash  
useradd test  
echo -e "\033[1;31mUser test is created\033[0m"  
echo magedu | passwd --stdin test &> /dev/null  
echo -e "\033[1;31mPasswd: magedu\033[0m"  
passwd -e test      #登陆后立即改密码  

颜色  
    \033[1,30m  #灰  
    \033[1,31m  #红  
    \033[1,32m  #绿  
    \033[1,33m  #黄  
    \033[1,34m  #蓝  
    \033[1,35m  #粉  
    \033[1,36m  #青  
    \033[1,37m  #白 
    
bash   
-n  #检查语法  
-x  #每一行的执行结果    

练习

1、 编写脚本 systeminfo.sh,显示当前主机系统信息,包括主机名,IPv4地址, 操作系统版本,内核版本,CPU型号,内存大小,硬盘大小

vim systeminfo.sh

#!/bin/bash  
COLOR="\033[1;35m"  
COLOREND="\033[0m"  
echo -e "OS: $COLOR`cat /etc/redhat-release`$COLOREND"  
echo -e "Kernel ver: $COLOR`uname -r`$COLOREND"  
echo -e "IP addr: $COLOR`ifconfig eth0 | grep -Eo '[0-9.]{7,15}' | head -1`$COLOREND"  
echo -e "CPU: $COLOR`lscpu | grep 'Model name' | cut -d: -f2 | tr -s " "`$COLOREND"  
echo -e "Disk space: $COLOR`lsblk | grep disk | tr -s " " | cut -d' ' -f4`$COLOREND"  
echo -e "Memory szie: $COLOR`free -h | grep Mem | tr -s ' ' | cut -d' ' -2`$COLOREND"  
unset COLOR COLOREND  
2、编写脚本 backup.sh,可实现每日将/etc/目录备份到/backup/etcYYYY-mm-dd中

vim backup.sh

#!/bin/bash  
cp -pr /etc /backup/etc`date +%F`  
3、编写脚本 disk.sh,显示当前硬盘分区中空间利用率最大的值

vim disk.sh

#!/bin/bash  
df -h | tr -s " " % | cut -d% -f5 | tr -s "Use" " " | sort -nr | head -1  
4、编写脚本 links.sh,显示正连接本主机的每个远程主机的IPv4地址和连接数,并按连接数从大到小排序

vim links.sh

#!/bin/bash  
netstat -nt | tr -s " " : | cut -d: -f6 | uniq -c | grep -v "Foreign" | sort -nr      

环境变量

变量声明、赋值:  
    export name=VALUE  
    declare -x name=VALUE  
变量引用:  
    $name, ${name}  
显示所有环境变量:  
    env  
    printenv  
    export  
    declare -x  
删除变量:  
    unset name  
  
echo $BASHPID   #本shell的进程ID  
echo $PPID      #父shell的进程ID  
echo $EDITOR    #默认编辑器  
  
bash内建的环境变量  
    PATH  
    SHELL  
    USER  
    UID  
    HOME  
    PWD  
    SHLVL       #shell嵌套层数  
    LANG  
    MAIL  
    HOSTNAME  
    HISTSIZE  
    _ 下划线  

只读和位置变量

只读变量:只能声明,但不能修改和删除  
    声明只读变量:  
        readonly name  
        declare -r name  
    查看只读变量:  
        readonly -p  
位置变量:在脚本代码中调用通过命令行传递给脚本的参数  
    $1, $2, ... 对应第1、第2等参数,shift [n]换位置  
    $0 命令本身  
    $* 传递给脚本的所有参数,全部参数合为一个字符串  
    $@ 传递给脚本的所有参数,每个参数为独立字符串  
    $# 传递给脚本的参数的个数  
注意:$@ $* 只在被双引号包起来的时候才会有差异  
    set -- 清空所有位置变量  
  
    ()      #只生效一次,相当于开启一个子shell  
    {}      #不开启子shell  
        (umask 066;touch f1.txt)  
      
    TITLE=ceo  
    ( echo $TITLE;TITILE=coo;echo $TITLE )  
    echo $TITLE  
  
vim scp.sh  
    #!/bin/bash  
    echo "uploading......"  
    scp -r $* wang@172.16.128.163:/data/script37  
    echo 'upload complete!'  

退出状态

进程使用退出状态来报告成功或失败  
    0 代表成功,1-255代表失败  
    $? 变量保存最近的命令退出状态  
例如:  
    ping -c1 -W1 hostdown &> /dev/null  
    echo $?  

退出状态码

bash自定义退出状态码  
    exit [n]:自定义退出状态码  
    注意:脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字  
    注意:如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码        

算术运算

bash中的算术运算:help let  
    +, -, *, /, %取模(取余), **(乘方),乘法符号有些场景中需要转义  
    实现算术运算:  
    (1) let var=算术表达式  
    (2) var=$[算术表达式]  
    (3) var=$((算术表达式))  
    (4) var=$(expr arg1 arg2 arg3 ...)  
    (5) declare –i var = 数值  
    (6) echo ‘算术表达式’ | bc  
bash有内建的随机数生成器变量:$RANDOM(0-32767)  
    示例:生成 0 - 49 之间随机数  
    echo $[$RANDOM%50]    

赋值

增强型赋值:  
    +=, -=, *=, /=, %=  
let varOPERvalue  
    例如:let count+=3  
    自加3后自赋值  
自增,自减:  
    let var+=1  
    let var++  
    let var-=1  
    let var--  

练习

1、编写脚本 sumid.sh,计算/etc/passwd文件中的第10个用户和第20用户的UID之和
#!/bin/bash  
UID10=`cat /etc/passwd | head | tail -1 | cut -d: -f3`  
UID20=`cat /etc/passwd | head -20 | tail -1 | cut -d: -f3`  
echo "UID10+UID20 = `echo $UID10+$UID20 | bc`"   
2、编写脚本 sumspace.sh,传递两个文件路径作为参数给脚本,计算这两个文件中所有空白行之和
#!/bin/bash  
FILE1=`grep "^$" $1 | wc -l`  
FILE2=`grep "^$" $2 | wc -l`  
NUM=$[ $FILE1+$FILE2 ]  
echo "两个文件的空行之和:$NUM"         
3、编写脚本 sumfile.sh,统计/etc, /var, /usr 目录中共有多少个一级子目录和文件
#!/bin/bash  
DIRS=`ls -ld /{etc,var,usr}/* | grep "^d" | wc -l`                                                                             
FILES=`ls -ld /{etc,var,usr}/* | grep -v "^d" | wc -l`  
echo "Directory: $DIRS FILES: $FILES"         

逻辑运算

true, false  
    1, 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 ! true  
    ! 0 = 1 ! false  
短路运算  
    短路与(&&)  
    第一个为0,结果必定为0  
    第一个为1,第二个必须要参与运算  
    短路或(||)  
    第一个为1,结果必定为1  
    第一个为0,第二个必须要参与运算  
异或:^  
    异或的两个值,相同为假,不同为真  
      
x=10;y=12;x=$[x^y];y=$[x^y];x=$[x^y];echo x=$x;echo$ y=$y  
    结果为:x=12,y=10  
      
短路与  
    cmd1 && cmd2        #cmd1正确执行才执行cmd2,cmd1错误执行则不执行cmd2  
      
短路或  
    cmd1 || cmd2        #cmd1执行错误才执行cmd2          

条件测试

判断某需求是否满足,需要由测试机制来实现  
    专用的测试表达式需要由测试命令辅助完成测试过程  
评估布尔声明,以便用在条件性执行中  
    • 若真,则返回0  
    • 若假,则返回1  
测试命令:  
    • test EXPRESSION  
    • [ EXPRESSION ]  
    • [[ EXPRESSION ]]  
    注意:EXPRESSION前后必须有空白字符  

bash的数值测试

-v VAR  
    变量VAR是否设置  
数值测试:  
    -gt 是否大于  
    -ge 是否大于等于  
    -eq 是否等于  
    -ne 是否不等于  
    -lt 是否小于  
    -le 是否小于等于    

bash的字符串测试

字符串测试:  
    = 是否等于  
    > ascii码是否大于ascii码  
    < 是否小于  
    != 是否不等于  
    =~ 左侧字符串是否能够被右侧的PATTERN所匹配  
    注意: 此表达式一般用于[[ ]]中;扩展的正则表达式  
    -z "STRING“ 字符串是否为空,空为真,不空为假  
    -n "STRING“ 字符串是否不空,不空为真,空为假  
注意:用于字符串比较时的用到的操作数都应该使用引号  

Bash的文件测试

存在性测试  
    -a FILE:同 -e  
    -e FILE: 文件存在性测试,存在为真,否则为假  
存在性及类别测试  
    -b FILE:是否存在且为块设备文件  
    -c FILE:是否存在且为字符设备文件  
    -d FILE:是否存在且为目录文件  
    -f FILE:是否存在且为普通文件  
    -h FILE 或 -L FILE:存在且为符号链接文件  
    -p FILE:是否存在且为命名管道文件  
    -S FILE:是否存在且为套接字文件  
      
FILE=f.sh; [[ "$FILE" =~ \.sh$ ]] && echo sh    #判断.sh后缀  
  
ip:0-255.0-255.0-255.0-255  
    正则表达式  
        IP=^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$  
    或  
        IP=^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$  
    ifconfig | grep -Eo "$IP"  
      
#判断后缀是否为.sh,并判断是否有执行权限  
FILE=/data/test.sh; [[ "$FILE" =~ \.sh$ ]] && [ -x "$FILE" ] && $FILE  

Bash的文件权限测试

文件权限测试:  
    -r FILE:是否存在且可读  
    -w FILE: 是否存在且可写  
    -x FILE: 是否存在且可执行  
文件特殊权限测试:  
    -u FILE:是否存在且拥有suid权限  
    -g FILE:是否存在且拥有sgid权限  
    -k FILE:是否存在且拥有sticky权限  
      
    vim user08.sh  
    #/bin/bash  
    [ "$#" -eq 0 ] && echo "Username is empty" && exit 2  
    id "$1" &> /dev/null && echo "$1 is exist" && exit 3   
    useradd "$1" &> /dev/null && echo "$1 is create" || exit 4  
    echo magedu | passwd --stdin "$1" &> /dev/null || exit 5  
    passwd -e "$1" &> /dev/null || exit 6  

Bash的文件属性测试

文件大小测试:  
    -s FILE: 是否存在且非空  
文件是否打开:  
    -t fd: fd 文件描述符是否在某终端已经打开  
    -N FILE:文件自从上一次被读取之后是否被修改过  
    -O FILE:当前有效用户是否为文件属主  
    -G FILE:当前有效用户是否为文件属组  

Bash的文件属性测试

双目测试:  
    FILE1 -ef FILE2: FILE1是否是FILE2的硬链接  
    FILE1 -nt FILE2: FILE1是否新于FILE2(mtime)  
    FILE1 -ot FILE2: FILE1是否旧于FILE2   

Bash的组合测试条件

第一种方式:  
    EXPRESSION1 -a EXPRESSION2 并且  
    EXPRESSION1 -o EXPRESSION2 或者  
    ! EXPRESSION  
    必须使用测试命令进行,[[ ]] 不支持  
第二种方式:  
    COMMAND1 && COMMAND2 并且,短路与,代表条件性的AND THEN  
    COMMAND1 || COMMAND2 或者,短路或,代表条件性的OR ELSE  
    ! COMMAND 非  
    如:[ -f “$FILE” ] && [[ “$FILE”=~ .*\.sh$ ]]  

练习

1、 编写脚本 argsnum.sh,接受一个文件路径作为参数;如果参数个数小于1, 则提示用户“至少应该给一个参数”,并立即退出;如果参数个数不小于1,则显示第一个参数所指向的文件中的空白行数
#!/bin/bash  
SpaceLine=`grep '^$' $1 | wc -l`                                                                                                                               
[ "$#" -lt 1 ] &&  echo "至少应该给一个参数" && exit 2  
[ -s "$1" ] && echo "$1 Space Lines:$SpaceLine" || echo "$1 is not exist"  
2、 编写脚本 hostping.sh,接受一个主机的IPv4地址做为参数,测试是否可连
通。如果能ping通,则提示用户“该IP地址可访问” ;如果不可ping通,则提示用户“该IP地址不可访问”  
      
#!/bin/bash  
IP="192.168.139.2"  
ping -c1 $IP &> /dev/null  
[ "$?" -eq 0 ] && echo "${IP} 地址可访问"  || echo "${IP} 地址不可访问"  
3、 编写脚本 checkdisk.sh,检查磁盘分区空间和inode使用率,如果超过80%,就发广播警告空间将满
#!/bin/bash  
SPACE=80  
DISK=`df -h | grep "/dev/sd" | tr -s " " % | cut -d% -f5 | sort -nr | head -1`  
[ $DISK -ge $SPACE ] && echo "Warring: `hostname` the disk space is $DISK" || echo "`hostname` the disk is Normol"  
4、 编写脚本 per.sh,判断当前用户对指定参数文件,是否不可读并且不可写
#!/bin/bash  
[ "$1" = "" ] && echo "Please input the file" && exit 2                                                                     
[ ! -f "$1" ] && echo "File is not exist" && exit 3  
[ -r "$1" -a -w "$1" ] && echo "`whoami` is read and write." || echo "`whoami` is not read and wrtie."  
5、 编写脚本 excute.sh ,判断参数文件是否为sh后缀的普通文件,如果是,添加所有人可执行权限,否则提示用户非脚本文件
#!/bin/bash  
[ "$#" -eq 0  ] && echo "Please Input file's name" && exit 2                                                                   
[[ "$1" =~ \.sh$ ]] && [ -f "$1" ] && chmod +x $1 && echo "Excute is added" || echo "The file is not script."   
6、 编写脚本 nologin.sh和 login.sh,实现禁止和允许普通用户登录系统

vim nologin.sh

#!/bin/bash  
[ "$1" = "" ] && echo "Please input username" && exit 2  
[[ "$1" =~ ^[0-9] ]] && echo "The username is wrong" && exit 3  
id $1 &> /dev/null  
[ "$?" -ne 0 ]  && echo "$1 is not exist" && exit 4   
passwd -l $1 &> /dev/null && echo "$1 is lock"  

vim login.sh

#!/bin/bash  
[ "$1" = "" ] && echo "Please input username" && exit 2  
[[ "$1" =~ ^[0-9] ]] && echo "The username is wrong" && exit 3  
id $1 &> /dev/null  
[ "$?" -ne 0 ]  && echo "$1 is not exist" && exit 4   
passwd -u $1 &> /dev/null && echo "$1 is unlock"              

使用read命令来接受输入

使用read来把输入值分配给一个或多个shell变量  
    -p 指定要显示的提示  
    -s 静默输入,一般用于密码  
    -n N 指定输入的字符长度N  
    -d ‘字符’ 输入结束符  
    -t N TIMEOUT为N秒  
    read 从标准输入中读取值,给每个单词分配一个变量  
    所有剩余单词都被分配给最后一个变量  
    read -p “Enter a filename: “ FILE  
  
read 变量1 变量2  
    read -p "Please input your name: " NAME  
    echo Your name is $NAME       
  
    echo 1 2 3 | { read x y z ; echo $x $y $z; }        #管道改变变量是在子shell里面,通过{}可以不开子shell  

条件选择if语句

选择执行:  
注意:if语句可嵌套  
单分支  
    if 判断条件;then  
        条件为真的分支代码  
    fi  
双分支  
    if 判断条件; then  
        条件为真的分支代码  
    else  
        条件为假的分支代码  
    fi  

if 语句

多分支  
    if 判断条件1; then  
        条件1为真的分支代码  
    elif 判断条件2; then  
        条件2为真的分支代码  
    elif 判断条件3; then  
        条件3为真的分支代码  
    else  
        以上条件都为假的分支代码  
    fi  
逐条件进行判断,第一次遇为“真”条件时,执行其分支,而后结束整个if语句  

条件判断:case语句

case 变量引用 in  
    PAT1)  
        分支1  
    ;;  
    PAT2)  
        分支2  
    ;;  
    ...  
    *)  
        默认分支  
    ;;  
esac  
  
case支持glob风格的通配符:  
    *: 任意长度任意字符  
    ?: 任意单个字符  
    []:指定范围内的任意单个字符  
    a|b: a或b  

练习

1、编写脚本 createuser.sh,实现如下功能:使用一个用户名做为参数,如果 指定参数的用户存在,就显示其存在,否则添加之;显示添加的用户的id号等信息
#!/bin/bash  
read -p "Please input the username: " USERNAME  
id $USERNAME &> /dev/null  
if [ "$?" -eq 0 ];then  
    echo "User is exist"  
    exit 2  
elif [[ "$USERNAME" =~ ^[0-9] ]];then                                                                                       
    echo "Username is not right"  
    exit 3  
else  
    useradd $USERNAME &> /dev/null  
    id $USERNAME  
fi        
2、编写脚本 yesorno.sh,提示用户输入yes或no,并判断用户输入的是yes还是no,或是其它信息
#!/bin/bash  
read -p "Please input yes or no: " VALUE  
case $VALUE in                                                                                                              
    [Yy]|[Yy][Ee][Ss])  
        echo yes  
    ;;  
    [Nn]|[Nn][Oo])  
        echo no  
    ;;  
    *)  
        echo "Input error"  
    ;;  
esac  
3、编写脚本 filetype.sh,判断用户输入文件路径,显示其文件类型(普通,目录,链接,其它文件类型)
#!/bin/bash  
read -p "Please input path: " PATH  
if [ "$PATH" = "" ];then  
    echo "No such file or directory"  
    exit 2  
elif [ -d "$PATH" -o -f "$PATH" ];then                                                                                      
    /usr/bin/ls -l $PATH  
else  
    echo "Please input vaild path"  
fi        
4、编写脚本 checkint.sh,判断用户输入的参数是否为正整数
#!/bin/bash  
read -p "Please input digit: " NUM  
if [[ "$NUM" =~ ^[0-9]+$ ]];then  
    /usr/bin/expr $NUM + 0 &> /dev/null                                                                                     
    [ "$?" -eq 0 ] && echo "It is integer" || echo "It is not integer"  
elif [[ "$NUM" =~ [.] ]];then  
    echo "It like not a integer"  
elif [[ "$NUM" =~ ^-[0-9]+$  ]];then  
    echo "It like a negative integer "  
else  
    echo "It like not digit"  
    exit 2  
fi  

bash如何展开命令行

● 把命令行分成单个命令词  
● 展开别名  
● 展开大括号的声明({})  
● 展开波浪符声明(~)  
● 命令替换$() 和 ``)  
● 再次把命令行分成命令词  
● 展开文件通配(*、 ?、 [abc]等等)  
● 准备I/0重导向(<、 >)  
● 运行命令  

防止扩展

反斜线(\)会使随后的字符按原意解释  
    echo Your cost: \$5.00  
    Your cost: $5.00  
加引号来防止扩展  
    • 单引号(’’ )防止所有扩展  
    • 双引号(” “)也可防止扩展,但是以下情况例外:  
    $(美元符号) 变量扩展  
    ` ` (反引号) 命令替换  
    \(反斜线) 禁止单个字符扩展  
    !(叹号) 历史命令替换  

bash的配置文件

按生效范围划分,存在两类:  
全局配置:  
    /etc/profile  
    /etc/profile.d/*.sh  
    /etc/bashrc  
个人配置:  
    ~/.bash_profile  
    ~/.bashrc  

shell登录两种方式

交互式登录:  
    (1)直接通过终端输入账号密码登录  
    (2)使用“su - UserName” 切换的用户  
执行顺序:/etc/profile --> /etc/profile.d/*.sh --> ~/.bash_profile -->  
~/.bashrc --> /etc/bashrc  
非交互式登录:  
    (1)su UserName  
    (2)图形界面下打开的终端  
    (3)执行脚本  
    (4)任何其它的bash实例  
执行顺序: /etc/profile.d/*.sh --> /etc/bashrc -->~/.bashrc  

Profile类

按功能划分,存在两类:  
    profile类和bashrc类  
profile类:为交互式登录的shell提供配置  
    全局:/etc/profile, /etc/profile.d/*.sh  
    个人:~/.bash_profile  
    功用:  
    (1) 用于定义环境变量  
    (2) 运行命令或脚本  

Bashrc类

bashrc类:为非交互式和交互式登录的shell提供配置  
    全局:/etc/bashrc  
    个人:~/.bashrc  
功用:  
    (1) 定义命令别名和函数  
    (2) 定义本地变量  

编辑配置文件生效

修改profile和bashrc文件后需生效  
两种方法:  
    1重新启动shell进程  
    2 . 或source  
例:  
    . ~/.bashrc   

Bash 退出任务

保存在~/.bash_logout文件中(用户)  
在退出登录shell时运行  
用于  
    创建自动备份  
    清除临时文件  

set命令

$- 变量  
h:hashall,打开这个选项后,Shell 会将命令所在的路径hash下来,避免每次都要查询。通过set +h将h选项关闭  
i:interactive-comments,包含这个选项说明当前的 shell是一个交互式的shell。所谓的交互式shell,在脚本中,i选项是关闭的。  
m:monitor,打开监控模式,就可以通过Job control来控制进程的停止、继续,后台或者前台执行等。  
B:braceexpand,大括号扩展  
H:history,H选项打开,可以展开历史列表中的命令,可以通过!感叹号来完成  
    例如“!!” 返回上最近的一个历史命令,“!n” 返回第 n 个历史命令  

脚本安全

set 命令  
    -u 在扩展一个没有设置的变量时,显示错误信息  
        等同set -o nounset  
    -e 如果一个命令返回一个非0退出状态值(失败)就退出  
        等同set -o errexit  
          
    -u  #变量没声明不能用  
    -e  #有错误就退出不执行后面的语句  
      
    set +o history  #不记录历史  
    set -o history  #记录历史  

练习

1、让所有用户的PATH环境变量的值多出一个路径,例如:/usr/local/apache/bin

vim /etc/profile.d/env.sh

PATH=$PATH:/usr/local/apache/bin  
2、用户 root 登录时,将命令指示符变成红色,并自动启用如下别名:

vim ~/.bashrc

rm='rm –i'  
cdnet='cd /etc/sysconfig/network-scripts/'  
editnet='vim /etc/sysconfig/network-scripts/ifcfg-eth0'  
#editnet='vim /etc/sysconfig/network-scripts/ifcfg-eno16777736 或 ifcfg-ens33' (如果系统是CentOS7)  
  
VERSION=`grep -Eo "\<[0-9]+\>" /etc/redhat-release | head -1`  
PS1="\[\e[31m\][\u@\h \W]\\$\[\e[0m\]"  
alias rm='rm –i'  
alias cdnet='cd /etc/sysconfig/network-scripts/'              
case $VERSION in  
    6)  
        alias editnet='vim /etc/sysconfig/network-scripts/ifcfg-eth0'                     
    ;;  
    7|8)  
        alias editnet='vim /etc/sysconfig/network-scripts/ifcfg-eno33'  
    ;;  
    *)                    
    ;;  
esac  
3、任意用户登录系统时,显示红色字体的警示提醒信息“Hi,dangerous!”

vim /etc/motd

^[[1;34mHi,dangerous!   #^[等于ascii码的1B,编辑模式按ctrl+v再按ESC可以打出来  
4、编写生成脚本基本格式的脚本,包括作者,联系方式,版本,时间,描述等

vim ~/.vimrc

set tabstop=4         
set ignorecase        
set cursorline        
set autoindent        
autocmd BufNewFile *.sh exec ":call SetTitle()"  
func SetTitle()  
    if expand("%:e") == 'sh'  
        call setline(1,"#!/bin/bash")   
        call setline(2,"#")   
        call setline(3,"#********************************************************************")   
        call setline(4,"#Author:        LiangJC")   
        call setline(5,"#QQ:            184116857")   
        call setline(6,"#Date:          ".strftime("%Y-%m-%d"))  
        call setline(7,"#FileName:      ".expand("%"))  
        call setline(8,"#URL:           http://www.magedu.com")  
        call setline(9,"#Description:       The test script")   
        call setline(10,"#Copyright (C):    ".strftime("%Y")." All rights reserved")  
        call setline(11,"#********************************************************************")   
        call setline(12,"")   
    endif  
endfunc  
autocmd BufNewFile * normal G  
5、编写用户的环境初始化脚本reset.sh,包括别名,登录提示符,vim的设置,环境变量等
#!/bin/bash       
#环境变量  
echo "PATH=$PATH:/usr/local/apache/bin &>> /etc/profile.d/env.sh"  
  
#提示符及别名  
VERSION=`grep -Eo "\<[0-9]+\>" /etc/redhat-release | head -1`  
PS1="\[\e[31m\][\u@\h \W]\\$\[\e[0m\]"  
alias rm='rm –i'  
alias cdnet='cd /etc/sysconfig/network-scripts/'              
case $VERSION in  
    6)  
        alias editnet='vim /etc/sysconfig/network-scripts/ifcfg-eth0'                     
    ;;  
    7|8)  
        alias editnet='vim /etc/sysconfig/network-scripts/ifcfg-eno33'  
    ;;  
    *)                    
    ;;  
esac  
  
#用户登陆信息  
echo "^[[1;34mHi,dangerous!" > /etc/motd  
  
#vim格式  
cat > /etc/skel/.vimrc <<EOF  
set tabstop=4         
set ignorecase        
set cursorline        
set autoindent        
autocmd BufNewFile *.sh exec ":call SetTitle()"  
func SetTitle()  
    if expand("%:e") == 'sh'  
        call setline(1,"#!/bin/bash")   
        call setline(2,"#")   
        call setline(3,"#********************************************************************")   
        call setline(4,"#Author:        LiangJC")   
        call setline(5,"#QQ:            184116857")   
        call setline(6,"#Date:          ".strftime("%Y-%m-%d"))  
        call setline(7,"#FileName:      ".expand("%"))  
        call setline(8,"#URL:           http://www.magedu.com")  
        call setline(9,"#Description:       The test script")   
        call setline(10,"#Copyright (C):    ".strftime("%Y")." All rights reserved")  
        call setline(11,"#********************************************************************")   
        call setline(12,"")   
    endif  
endfunc  
autocmd BufNewFile * normal G  
EOF  
USERNAME=`whoami`  
cp -a /etc/skel/.vimrc /$USERNAME/  
  
echo "初始化成功!"    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容

  • 第 2 章 SHELL 基础知识2.1 shell脚本我们在上面简单介绍了一下什么是shell脚本,现在我们来进一...
    LiWei_9e4b阅读 1,566评论 0 0
  • 官网 中文版本 好的网站 Content-type: text/htmlBASH Section: User ...
    不排版阅读 4,380评论 0 5
  • 一、shell脚本介绍 1.1 开头(环境使用shebang机制) #!/bin/bash 必须写在文件首行 符号...
    优果馥斯阅读 3,284评论 0 1
  • bash中变量的种类 局部变量:生效范围为当前shell进程,对当前shell之外的其它shell进程,包括当前s...
    毛利卷卷发阅读 405评论 0 1
  • 建立一个脚本 Linux中有好多中不同的shell,但是通常我们使用bash (bourne again shel...
    某人在阅读 2,006评论 0 0