Shell——handbook

SHELL脚本

概念

shell( shell script, 程序化脚本 ), shell脚本是利用shell的功能所写的一个程序。这个程序是使用纯文本文件, 将一些shell语法与命令( 含外部命令 )写在里面, 搭配正则表达式/管道命令与数据流重定向等功能,以达到我们所想要的处理目的


学习目的

  • 自动化管理的重要根据
  • 跟踪与管理系统的重要工作
  • 简单入侵检测功能
  • 连续命令单一化
  • 简易的数据处理
  • 跨平台支持与学习历程较短

注意事项

  • 命令是重上而下,从左而右地分析与执行
  • 命令/选项与参数间的多个空格都会被忽略掉
  • 空白行也将被忽略掉,并且[ Tab ]按键所产生的空白同样视为空格键
  • 如果读取到一个Enter符号( CR ),就尝试开始执行该行( 或该串 )命令
  • 至于如果一行的内容太多,则可以使用【\[Enter]】来扩展至下一行
  • 【 # 】可做为注释,任何加在 # 后面的数据将全部被视为注释文字而被忽略

执行方法

直接命令执行

shell.sh 文件必要要具备可读与可执行( rx )的权限

绝对路径

使用 /home/dmtsai/shell.sh 来执行命令

相对路径

假设工作目录在 /home/dmtsai/, 则使用./shell.sh来执行

变量【 path 】功能

将shell.sh放在PATH指定的目录内,例如~/bin/

以bash程序来执行

通过bash shell.shsh shell.sh来执行


*Hello World!

#!/bin/bash
# Program:
#       this program shows "Hello World" in your screen.
# History:
# 2019/08/04    xiaozuo         First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "Hello World! \a \n"
exit 0


编写规范

  • 脚本的功能
  • 脚本的版本信息
  • 脚本的作者与联络方式
  • 脚本的版权声明方式
  • 脚本的History历史记录
  • 脚本内较特殊的命令,使用【绝对路径】的方式来执行
  • 脚本运行时需要的环境变量预先声明与设置

范例

*交互式脚本

#!bin/bash
# Program:
#       User input his first name and last name. Program shows his full name.
# History:
# 2019/08/04    xiaozuo         First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "Please input your first name: " firstname   # 提示用户输入姓
read -p "Please input your last name: " lastname     # 提示用户输入名
echo -e "\nYour full name is: ${firstname} ${lastname}" # 输出全名

*随日期变化

#!bin/bash
# Program:
#       Program creates 3 files, which named by user's input and data command
# History
# 2019/08/04    xiaozuo         First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
# 让使用者输入文件名称, 并取得fileuser这个变量
echo -e "I will use 'touch' command to create 3 files."
read -p "Please input your filename: " fileuser
filename=${fileuser:-"filename"} # 判断是否设置文件名
date1=$(date --date='2 days ago' +%Y%m%d)
date2=$(date --date='1 days ago' +%Y%m%d)
date3=$(date +%Y%m%d)
file1=${filename}${date1}
file2=${filename}${date2}
file3=${filename}${date3}
# 创建
touch "${file1}"
touch "${file2}"
touch "${file3}"
                       

数值运算

*var=$(( 运算内容 ))

#!/bin/bash
# Program:
#       user input 2 integer numbers; program will cross these tuo number.
# History:
# 2019/08/04    xiaozuo         First realease
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "Please input two numbers, I will multiplying them! \n"
read -p "first number: " first
read -p "second number: " second
total=$((${first}*${second}))
echo -e "\nThe result of ${first} x ${second} is ==> ${total}"

*计算含有小数点的数据

bc

echo "123.23*89.89" | bc

*计算Pi

此脚本可将CPU调到高负载状态

#!/bin/bash
# Program:
#       User input a scale number to calculate pi number.
# History:
# 2019/08/04    xiaozuo         First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "THis program will calculate pi value. \n"
echo -e "You should input a float number to calculate pi value.\n"
read -p "The scale number (10~10000) ?" checking  # 计算几个小数点
num=${checking:-"10"} # 开始判断是否有输入值
echo -e "Starting calculate pi value. Be patient."
time echo "scale=${num}; 4*a(1)" | bc -lq  # 4*a(1)是bc主动提供的计算pi的函数


执行方式差异

直接执行

利用bash或者sh来执行脚本的时候, 该脚本都会使用一个新的bash环境来执行脚本内的命令,相当于是在子进程的bash内执行,在子进程完成后,在子进程内的各项变量或操作将会结束而不会传到父进程中


利用source执行

source shell.sh是在父进程运行的,各项操作都会在原本的bash内生效

例如source ~/.bashrcsource ~/.zshrc


判断式

test

# 检查/dmtsai是否存在
test -e /dmtsai && echo "exist" || echo "not exist"
# not exist
测试参数 说明
-e 该【文件名】是否存在
-f 该【文件名】是否存在且为文件(file)
-d 该【文件名】是否存在且为目录(directory)
-z [string] 判定字符串是否为0,若string为空,返回True
! 反相状态,当file不具有某参数时, 返回True
-o 两条件任意一个成立,返回True
-a 两条件同时成立,返回True
-gt n1 大于 n2
-lt n1 小于 n2
-eq 两数值相等
-ne 两数值不相等

*判断文件属性

#!/bin/bash
# Program
#       user input a filename, program will check the flowing:
#       1.) exist? 2.) file/directory 3.) file permissions
# History:
# 2019/08/05     xiaozuo        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "Please input a filename, I will check the filename's type and permission. \n\n"
read -p "Inpur a filename: " filename
# 判断用户是否有输入
test -z ${filename} && "You MUST input a filename." && exit 0
# 判断文件是否存在
test ! -e ${filename} && echo "The filename '${filename}' DO NOT exist" && exit 0
# 判断文件属性
test -f ${filename} && filetype="regulare file"
test -d ${filename} && filetype="directory"
test -r ${filename} && perm="readable"
test -w ${filename} && perm="${perm}  writable"
test -x ${filename} && perm="${perm} executable"
# 输出
echo "The filename: ${filename} is a ${filetype}"
echo "And the permissions for you are: ${perm}"


判断符号[]

中括号的两端都需要空格符来分开

 [ -z "${HOME}" ] ; echo $?	

  • 中括号内的每个组件都需要有空格来分隔
  • 中括号内的变量, 最好都以双引号括起来
  • 中括号内的常数, 最好都以单或双引号括起来

*判断用户输入

#!/bin/bash
# Program:
#       This program will show the user's choice
# History:
# 2019/08/05    xiaozuo         First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "Please input (Y/N) : " yn
[ "${yn}" == "Y" -o "${yn}" == "y" ] && echo "OK, continue" && exit 0
[ "${yn}" == "N" -o "${yn}" == "n" ] && echo "Oh interrupt!" && exit 0
echo "I don't know what your choice is" && exit 0



默认变量

表达式 说明
$# 代表后接的参数【个数】
$@ 代表【"$1" “$2” “$3” “$4”】,每个变量独立
$* 代表【 “$1 $2 $3 $4” 】

*可携带参数的脚本

#!/bin/bash
# Program:
#       Program shows the script name, parameters...
# History:
# 2019/08/05    xiaozuo         First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo "the script name is   ==> ${0}"
echo "Total parameter number is ==> $#"
# 判断参数是否小于2
[ "$#" -lt 2 ] && echo "The number of parameter is less than 2. Stop here." && exit 0
echo "Your whole parameter is ==> '$@'"
echo "The 1st parameter ==>${1}"
echo "The 2nd parameter ==>${2}"


shift

造成参数变量号码偏移

*偏移参数

#!/bin/bash
# Program:
#       Program shows the effect of shift function
# History:
# 2019/08/05    xiaozuo         First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo "Total parameter number is ==> $#"
echo "Your whole parameter is ==> '$@'"
shift # 一个变量的shift
echo "Total parameter number is ==> $#"
echo "Your whole parameter is ==> '$@'"
shift 3 # 三个变量的shift
echo "Total parameter number is ==> $#"
echo "Your whole parameter is ==> '$@'"



条件判断式

if…then

单层判断

if 【条件判断式】; then

fi # if反过来写 代表结束

  • && 代表 and
  • || 代表 or

*逻辑判断用户输入

#!/bin/bash
# Program:
#       This program shows the user's choice with if then
# History:
# 2019/08/05    xiaozuo         First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "Please input (Y/N) : " yn
if [ "${yn}" == "Y" ] || [ "${yn}" == "y" ]; then
        echo "OK continue"
        exit 0
fi
if [ "${yn}" == "N" ] || [ "${yn}" == "n" ]; then
        echo "Oh, interrupt!"
        exit 0
fi
echo "I don't know what your choice is" && exit 0


多重判断

if [ 条件判断式一]; then

elif [条件判断式二]; then

else

fi


*多重逻辑判断用户输入

#!/bin/bash
# Program:
#       This program shows the user's choice with if else
# History:
# 2019/08/05    xiaozuo         First release
# 2019/08/05    xiaozuo         Second release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "Please input (Y/N) : " yn
if [ "${yn}" == "Y" ] || [ "${yn}" == "y" ]; then
        echo "OK continue"
elif [ "${yn}" == "N" ] || [ "${yn}" == "n" ]; then
        echo "Oh, interrupt!"
else
        echo "I don't know what your choice is"
fi
 


*提醒用户输入参数

#!/bin/bash
# Program:
#       Check $1 is equal to "hello"
# History:
# 2019/08/05    xiaozuo         First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
if [ "${1}" == "hello" ]; then
        echo "Hello, how are you ?"
elif [ "${1}" == "" ]; then
        echo "You MUST input parameters, ex> {${0} someword}"
else
        echo "The only parameter is 'hello',ex> {${0} hello}"
fi


*检测常见端口服务

获取目前主机启动的服务

# 下载net-tools
sudo apt install net-tools
# 查看目前主机启动的服务
netstat -tuln

  • 80:www
  • 22:ssh
  • 21:ftp
  • 25:mail
  • 111:RPC( 远程过程调用 )
  • 631:CUPS( 打印服务功能 )
#!/bin/bash
# Program:
#       Using netstat and grep to detect WWW,SSH,FTP,Mail services.
# History:
# 2019/08/05    xiaozuo         First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
# 提示信息
echo "Now, I will detect yout linux server's services!"
echo -e "The www, ftp, ssh, and mail (smtp) will be detect! \n"
# 开始检测
testfile=/dev/shm/netstat_checking.txt
# netstat -tuln > ${testfile}
testing=$(grep ":80 " ${testfile})
if [ "${testing}" != "" ]; then
        echo "WWW is running in your system."
fi
testing=$(grep ":22 " ${testfile})
if [ "${testing}" != "" ]; then
        echo "SSH is running in your system."
fi
testing=$(grep ":21 " ${testfile})
if [ "${testing}" != "" ]; then
        echo "FTP is running in your system."
fi
testing=$(grep ":25 " ${testfile})
if [ "${testing}" != "" ]; then
        echo "Mail is running in your system."
fi



case…in…esac

case $变量名称 in
"第一个变量内容")
	;;
"第二个变量内容")
	;;
*)
	exit 1
	;;
esac

*直接执行式

#!/bin/bash
# Program:
#       Show "Hello" from $1  by use case...in...esac
# History:
# 2019/08/05    xiaozuo         First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
case ${1} in
        "hello")
                echo "Hello, how are you ?"
                ;;
        "")
                echo "You MUST input parameters, ex> {${0} someword}"
                ;;
        *) # 相当于通配符, 0~无穷多个任意字符之意
                echo "Usage ${0} {hello}"
                ;;
esac


*交互式

#!/bin/bash
# Program:
#       This script only accepts the flowing parameter: one, two or three.
# Hisstory:
# 2019/08/05    xiaozuo         First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo "This program will print your selection !"
read -p "Input your choice: " choice
case ${choice} in
        "one")
                echo "Your choice is ONE"
                ;;
        "two")
                echo "Your choice is TWO"
                ;;
        "three")
                echo "Your choice is THREE"
                ;;
        *)
                echo "Usage ${0} {one|two|three}"
                ;;
esac



函数function

shell脚本的执行方式是由上而下,由左至右,因此在shell脚本当中的function的设置一定要在程序的最前面

function fname(){
	code...
}


*交互式函数版

#!/bin/bash
# Program:
#       This script only accepts the flowing parameter: one, two or three.
# Hisstory:
# 2019/08/05    xiaozuo         First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
function showname(){
        echo -n "Your choice is " # 可以不换行继续在同一行显示
}
echo "This program will print your selection !"
case ${1} in
        "one")
                showname; echo ${1} | tr 'a-z' 'A-Z' # 将参数做大小写转换
                ;;
        "two")
                showname; echo ${1} | tr 'a-z' 'A-Z'
                ;;
        "three")
                showname; echo ${1} | tr 'a-z' 'A-Z'
                ;;
        *)
                echo "Usage ${0} {one|two|three}"
                ;;
esac



源码阅读

阅读目录:/etc/init.d

*内置变量

#!/bin/bash
# Program:
#       This script only accepts the flowing parameter: one, two or three.
# Hisstory:
# 2019/08/05    xiaozuo         First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
function showname(){
        echo "Your choice is ${1}" # 这里的${1}必须参考下面命令的执行
}
echo "This program will print your selection !"
case ${1} in
        "one")
                showname 1 # 函数后 接有参数
                ;;
        "two")
                showname 2
                ;;
        "three")
                showname 3
                ;;
        *)
                echo "Usage ${0} {one|two|three}"
                ;;
esac



循环(loop)

while…do…done

while [ condition ] # 当满足condition时就执行循环
do
	code
done


until…do…done

until [ condition ] # 当满足condition时终止循环
do
	code
done

*交互式案例

  • while...do...done
#!/bin/bash
# Program:
#       Repeat question until user input correct answer.
# History:
# 2019/08/05    xiaozuo         First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
while [ "${yn}" != "yes" -a "${yn}" != "YES" ] # attention
do
        read -p "Please input yes/YES to stop this program: " yn
done
echo "OK! you input the correct answer."


  • until...do...dine
#!/bin/bash
# Program:
#       Repeat question until user input correct answer.
# History:
# 2019/08/05    xiaozuo         First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
until [ "${yn}" == "yes" -o "${yn}" == "YES" ]  # attention
do
        read -p "Please input yes/YES to stop this program: " yn
done
echo "OK! you input the correct answer."



*计算1-100之合和

#!/bin/bash
# Program:
#       Use loop to calculate "1-100" result.
# History:
# 2019/08/05    xiaozuo         First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
sum=0
i=0
while [ "${i}" != "100" ] # until [ "${i}" == "100" ]
do
        i=$(($i+1))
        sum=$(($sum+$i))
done
echo "The result of '1-100' is ==> $sum"



for…do…done(固定循环)

已知循环次数

for var in con1 con2 con3 ...
do
	code
done

*输出动物种类

#!/bin/bash
# Program:
#       Using for...loop to print 3 animal
# History:
# 2019/08/05    xaiozuo         First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/loacl/sbin:~/bin
export PATH
for animal in cat dog bird
do
        echo "The animal is ${animal}"
done



*检查系统账号

#!/bin/bash
# Program:
#       Use id, finger command to check system account's information.
# History:
# 2019/08/05    xiaozuo         First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
users=$(cut -d ':' -f1 /etc/passwd)     # 选取账号名称
for username in ${users}
do
        id ${username}
done



*利用连续数字seq进行ping检测

#!/bin/bash
# Program:
#       Use PING command to check the network's PC state.
# History:
# 2019/08/05    xiaozuo         First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
network="127.0.0"       # 定义一个域名的前面部分
for sitenu in $(seq 1 100) # seq为sequence(连续)的缩写
do
        # 下面的程序在获取PING的返回值是正确的还是错误的
        ping -c 1 -w 1 ${network}.${sitenu} &> /dev/null && result=0 || result=1
        # 开始显示结果是正确的启动(UP)还是错误的没有连通(DOWN)
        if [ "${result}" == 0 ]; then
                echo "Server ${network}.${sitenu} is UP."
        else
                echo "Server ${network}.${sitenu} is DOWN."
        fi
done
    


*检测目录内文件名的权限

#!/bin/bash
# Program:
#       Use input dir name, check the permission of files.
# History:
# 2019/08/05    xiaozuo         First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
# 先检测是否存在
read -p "Please input a directory: "  dir
if [ "${dir}" == "" -o ! -d "${dir}" ]; then
        echo "The ${dir} is NOT exist."
        exit 1
fi
# 开始测试文件
filelist=$(ls ${dir})
for filename in ${filelist}
do
        perm=""
        test -r "${dir}/${filename}" && perm="${perm} readable"
        test -w "${dir}/${filename}" && perm="${perm} writable"
        test -x "${dir}/${filename}" && perm="${perm} executable"
        echo "The file ${dir}/${filename}'s permission is ${perm}"
done



for…do…done(数值处理)

for (( 初始值; 限制值; 赋值运算 ))
do
	code
done

  • 初始值:某个变量在循环当中的初始值i=1
  • 限制值:当某变量的值在这个限制值的范围内,就继续循环i<100
  • 赋值运算:每做一个循环,变量就变化i++

*1-任意值求和

#!#!/bin/bash
# Program:
#       Use for to calculate 1-?
# History:
# 2019/08/05    xiaozuo         First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "Please input a number, I will count for 1-?" num
sum=0
for (( i=1; i<=${num}; i++ ))
do
        sum=$((${sum}+${i}))
done
echo "The result of '1-${num}' is ==> ${sum}"



随机数与数组

*随机选取

#!/bin/bash
# Program:
#       try to decide what you eat
# History;
# 2019/08/05    xiaozuo         First release
# 2019/08/05    xiaozuo         Second release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
eat[1]="手抓饼"
eat[2]="快乐套餐"
eat[3]="方便面"
eat[4]="面条"
eat[5]="火锅"
eat[6]="串串"
eat[7]="奶茶"
eat[8]="瓜子"
eat[9]="不吃"
eatnum=9
# 只输出一个值
# check=$(( ${RANDOM} * ${eatnum}/32767+1 ))
# echo "you may eat ${eat[${check}]}"
eated=0
while [ "${eated}" -lt 3 ]; do
        check=$(( ${RANDOM} * ${eatnum} / 32767 + 1 ))
        mycheck=0
        if [ "${eated}" -ge 1 ]; then
                for i in $(seq 1 ${eated})
                do
                        if [ ${eatedcon[$i]} == $check ]; then
                                mycheck=1
                        fi
                done
        fi
        if [ ${mycheck} == 0 ]; then
                echo "you may eat ${eat[${check}]}"
                eated=$((${eated} + 1))
                eatedcon[${eated}]=${check}
        fi
done



跟踪与调试

sh [-nvx] shell.sh

-n : 不要执行脚本,仅检查语法问题
-v : 执行脚本前,先将脚本文件内容输出到屏幕上
-x : 将使用到的脚本内容显示到屏幕上(重要)

bash -x random.sh

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/home/xz/bin
+ export PATH
+ eat[1]=手抓饼
+ eat[2]=快乐套餐
+ eat[3]=方便面
+ eat[4]=面条
+ eat[5]=火锅
+ eat[6]=串串
+ eat[7]=奶茶
+ eat[8]=瓜子
+ eat[9]=不吃
+ eatnum=9
+ eated=0
+ '[' 0 -lt 3 ']'
+ check=3
+ mycheck=0
+ '[' 0 -ge 1 ']'
+ '[' 0 == 0 ']'
+ echo 'you may eat 方便面'
you may eat 方便面
+ eated=1
+ eatedcon[${eated}]=3
+ '[' 1 -lt 3 ']'
+ check=1
+ mycheck=0
+ '[' 1 -ge 1 ']'
++ seq 1 1
+ for i in $(seq 1 ${eated})
+ '[' 3 == 1 ']'
+ '[' 0 == 0 ']'
+ echo 'you may eat 手抓饼'
you may eat 手抓饼
+ eated=2
+ eatedcon[${eated}]=1
+ '[' 2 -lt 3 ']'
+ check=5
+ mycheck=0
+ '[' 2 -ge 1 ']'
++ seq 1 2
+ for i in $(seq 1 ${eated})
+ '[' 3 == 5 ']'
+ for i in $(seq 1 ${eated})
+ '[' 1 == 5 ']'
+ '[' 0 == 0 ']'
+ echo 'you may eat 火锅'
you may eat 火锅
+ eated=3
+ eatedcon[${eated}]=5
+ '[' 3 -lt 3 ']'


你可能感兴趣的:(笔记)