Bash 的编程

前言:/etc/passwd文件保存了每一个系统用户和登入用户的基本信息,你是否真正了解它每一个字段的意义呢?


  • 详细讲解/etc/passwd文件内容
查看/etc/passwd文件的内容

[root@7 ~]# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
avahi-autoipd:x:170:170:Avahi IPv4LL Stack:/var/lib/avahi-autoipd:/sbin/nologin
systemd-bus-proxy:x:999:997:systemd Bus Proxy:/:/sbin/nologin
systemd-network:x:998:996:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:997:995:User for polkitd:/:/sbin/nologin
abrt:x:173:173::/etc/abrt:/sbin/nologin
第一字段:用户名(也被称为登录名);在上面的例子中,我们看到系统中所有的用户

第二字段:密码;在/etc/passwd中我们看到的是一个x,其实密码已被映射到/etc/shadow文件中

第三字段:UID

第四字段:GID

第五字段:用户名全称,这是可选的,可以不设置,这里其实就是在useradd命令创建用户的时候使用-c选项指定的用户的信息

第六字段:用户的家目录所在位置

第七字段:用户所用SHELL的类型,用户的shell有多种,可以是/bin/bash表示可以登入的用户;也可以是/sbin/nologin,一般用户设置系统用户或非登入用户

(二)bash编程权威指南3

前言:在任何编程语言中,为了控制代码的执行的顺序,都会有一些控制流语法,这些控制流语法就是我们通常所说的if else判断流,while、for in循环控制流等。那么今天我们就来看看这些控制流是如何来控制脚本中的代码的执行顺序的

  • (一)选择执行语句
  • 单分支的if语句
if 测试条件;then
如果满足条件就执行这里的代码
fi
  • 双分支的if语句
if 测试条件;then
如果满足条件就执行这里的代码
else
如果不满足条件就执行这里的代码
fi
  • 练习1:通过参数传递一个用户名给脚本,此用户不存在,则添加,密码与用户名相同
#!/bin/bash
# description
# 如果没有传递参数,直接退出
if [ $# -lt 1 ];
then
echo "please send argument"
exit 1
fi

if grep "^$1\>" /etc/passwd &>/dev/null;then
echo "user $1 exists"
else
useradd $1
echo $1 | passwd --stdin $1 &>/dev/null
echo " add user $1 "
fi
  • 练习2:通过参数传递一个用户名给脚本,此用户存在,则删除用户以及其家目录和邮件目录
#!/bin/bash
# description

if [ $# -lt 1 ];then
echo "please send a argument."
exit 1
fi

if id -u $1 &> /dev/null;then
userdel -r $1
echo "delete user $1"
else
echo "user $1 doesn't exists"
fi
  • 练习3:通过命令行参数给定两个数字,输出其中最大的数值
#!/bin/bash
# 通过命令行参数给定两个数字,输出其中最大的数值

if [ $# -lt 2 ];then
echo " Please input two arguments."
exit 1
fi

if [ $1 -eq $2 ];then
echo "$1 equals to $2"
exit 1
fi

if [ $1 -gt $2 ];then
echo " The big one is $1 "
else
echo " The big one is $2 "
fi
#!/bin/bash
# 通过命令行参数给定两个数字,输出其中最大的数值
if [ $# -lt 2 ];then
echo " Please input two arguments."
exit 1
fi

declare -i max=$1

if [ $max -lt $2 ];then
max=$2
fi

echo " The big one is $max "
  • 练习4:通过命令行参数给定一个用户名,判断其ID号是偶数还是奇数
#!/bin/bash
# 通过命令行参数给定一个用户名,判断其ID号是偶数还是奇数

if [ $# -lt 1 ];then
echo "Please input a argument."
exit 1
fi

if id -u $1 &> /dev/null;then
idNum=$(id -u $1)
let s=$idNum%2
# 判断余数是0还是1,如果是0,那么就是偶数,如果是1,就是奇数
if [ $s -eq 0 ];then
echo "$1'ID number is $idNum and oven"
else
echo "$1'ID number is $idNum and odd"
fi

else
echo "user $1 doesn't exits"
fi
  • 练习5:通过命令行参数给定两个两个文件名,如果某文件不存在,则结束脚本执行,如果都存在时返回两个文件的行数之和
#!/bin/bash
# 通过命令行参数给定两个两个文件名,如果某文件不存在,则结束脚本执行,如果都存在时返回两个文件的行数之和

if [ $# -lt 2 ];then
echo " Please input two arguments. "
exit 1
fi

if [ -e $1 -a -e $2 ];then
totalLines=$[ $(grep ".*" $1|wc -l)+$(grep ".*" $2|wc -l) ]
echo " The total lines are $totalLines "
else
echo " the file doesn't exists !"
exit 1
fi
  • (二)read命令:通过键盘输入数据,将键盘输入的值传递给脚本的中定义的变量,从而完成变量赋值操作 。脚本中使用read命令可以使得用户能够与程序进行交互
  • read命令的使用格式
# 这里变量名的意义在于将用户输入的值传递给这个变量名,那么在脚本中我们就可以拿到用户输入的值了
read [选项] [变量名]

选项:
-p (promp):在用户在键盘输入数据时,指定显示提示符
-t(timeout):指定超时时间,如果用户在指定的超时时间都没有输入的话,那么就终止脚本的执行
  • 示例:通过脚本创建一个用户,并设置密码,要求:需要用户交互式输入用户名和密码,如果用户名存在,终止脚本,如果不存在就创建,且设置密码
#!/bin/bash
#通过脚本创建一个用户,并设置密码,要求:需要用户交互式输入用户名和密码提示用户输入用户名
read -p "Enter a user name:" name
[ -z "$name" ] && echo "a user name is needed." && exit 1


# 如果用户输入用户名和密码完成,创建用户和密码
if id $name &>/dev/null;then
echo "$name already exists."
exit 2

else
# 如果用户输入用户名,提示用户输入密码
read -p "Enter a password for $name, default is 'password':" password
[ -z "$password" ] && password="password"
adduser $name
echo "$password" | password --stdin $name &>/dev/null
echo "Add user $name."
fi

(三)bash编程权威指南4(if嵌套和多分支)


  • (1)在运行脚本前,我们一般先对脚本的语法进行检查,如果脚本有bug,我们再调试脚本
  • 语法检查
bash -n 脚本路径
  • 脚本调试
bash -x 脚本路径
  • (2)过程式编程语言的执行流程
顺序执行
选择执行
循环执行
  • 选择执行
(1)&&,|| 
command1 && command2:如果command1正确,也执行command2;如果command1不正确,不执行command2
command1 || command2:如果command1正确,不执行command2;如果command1不正确,执行command2

(2)if语句
if语句:三种格式
我们之前已经学习了单分支的if语句
接下来学习if多分支和if嵌套语句

(3)case语句

  • if嵌套语句格式
if [ 条件 ];then
if [ 条件 ];then
执行这里的代码
fi
fi
if [ 条件 ];then
if [ 条件 ];then
执行这里的代码
fi
else
if [ 条件 ];then
执行这里的代码
fi
fi
  • if 多分支语句格式(注意:多个分支只会执行一个,执行首先为真的分支)
if [ 条件1 ];then
条件1为真执行这里的代码
elif [ 条件2 ];then
条件2为真执行这里的代码
elif [ 条件3 ];then
条件3为真执行这里的代码
.
.
.

elif [ 条件n ];then
条件n为真执行这里的代码
else
如果所有的条件都不满足时,执行这里的代码
fi

注意:多个分支只会执行一个,执行首先为真的分支
  • 示例代码演示:通过脚本参数传递一个文件路径给脚本,判断此文件的类型
#!/bin/bash
#通过给定的文件路径,判断文件是什么类型的文件

if [ $# -lt 1 ];then
echo "A argument is needed."
exit 1
fi

if ! [ -e $1 ];then
echo "No such file or directory."
exit 2
fi

if [ -f $1 ];then
echo "Common file"
elif [ -d $1 ];then
echo "Directory"
elif [ -L $1 ];then
echo "Symbolic link"
elif [ -b $1 ];then
echo "block special file"
elif [ -c $1 ];then
echo "character special file"
elif [ -S $1 ];then
echo "Socket file"
else
echo "Unknow"
fi
# 注意:if语句可以嵌套
  • 练习又来了
  • 写一个脚本
    (1)传递一个参数给脚本,此参数为用户名
    (2)根据其ID号来判断用户类型:0:管理员 ,1-499:系统用户,500+:为登入用户, 输出用户是那种类型的用户
#!/bin/bash
#(1)传递一个参数给脚本,此参数为用户名
# (2)根据其ID号来判断用户类型:0:管理员 ,1-999:系统用户,1000+:为登入用户

if [ $# -lt 1 ]; then
echo "A argument is needed."
exit 1
fi

# 如何输入的用户名存在
# 注意通过命令来判断成功与否不能加[]
if id -u $1 &>/dev/null;then
userid=$(id -u $1)

if [ $userid -eq 0 ];then
echo "Administrator"
elif [ $userid -gt 1 -a $userid -lt 499 ];then
echo " System user"
elif [ $userid -ge 500 ];then
echo "Login user"
fi

else
echo "NO such user."
fi
  • 写一个脚本
    (1)列出如下菜单给用户
    disk) show disks info
    mem)show memory info
    cpu)show cpu info
    *)quit
    (2)提示用户给出自己的选择,而后显示对应其选择的相应系统信息
#!/bin/bash
cat << EOF
disk) show disks info
mem)show memory info
cpu)show cpu info
*)quit
EOF

read -p "Please input your option:" option

# 如果用户输入的是大写,直接将大写转变为小写
option=$(echo $option | tr [A-Z] [a-z])

if [ $option == "disk" ]; then
fdisk -l
elif [ $option == "mem" ]; then
free -m
elif [ $option == "cpu" ]; then
lscpu
elif [ $option == "*" ]; then
exit 1
else
echo "Wrong option"
fi

(四)bash编程权威指南5(循环)

前言:循环就是将一段代码重复执行0,1次或多次。
进入条件:条件满足时,需要进入循环
退出条件:每个循环都应该有退出条件,有机会退出循环
bash的循环分类:
  • for循环
  • while循环
  • until循环

  • (1)for循环有两种格式 (重点必须掌握)
    变量列表
    • 第一种格式:遍历列表 (重点必须掌握)
    • 第二种格式:控制变量 (重点必须掌握)
for 变量 in 列表; do
循环体
done

进入条件:只要列表中有元素,即可进入循环
退出循环:列表中的元素遍历完成
  • 示例
#!/bin/bash
for username in user1 user2 user3;do
if id $username &>/dev/null;then
echo "$username is already exists."
else
useradd $username
echo "Add &username success"
done
  • 列表生成的方式
1)直接取出 
例如: user1 user2 user3
2)整数列表
例如 {1..100}
例如 `seq 1 2 100` 注意:中间的2表示步长
3)返回列表的命令
`ls /var/log/`
4)glob
例如: /var/log/*
(5)变量引用,如:@* ,
  • 示例1:求100内所有正整数之和
#!/bin/bash
# 求100内所有正整数之和

# 申明sum为正整数
declare -i sum=0

for i in {1..100}; do
sum=$[ $sum+$i ]
done

echo "$sum"
  • 示例2:列出/var/log/目录下所有文件的类型
#!/bin/bash
# 列出/var/log/目录下所有文件的类型

for option in /var/log/*; do

if [ -f $option ];then
echo "$option is Common file"
elif [ -d $option ];then
echo "$option is Directory"
elif [ -L $option ];then
echo "$option is Symbolic link"
elif [ -b $option ];then
echo "$option is block special file"
elif [ -c $option ];then
echo "$option is character special file"
elif [ -S $option ];then
echo "$option is Socket file"
else
echo "Unknow"
fi

done
  • 练习1:分别求100以内所有偶数之和,奇数之和
#!/bin/bash
# 1:分别求100以内所有偶数之和,奇数之和

declare -i oven=0
declare -i odd=0

for i in {1..100}; do
count=$[ $i % 2 ]

if [[ $count == 0 ]]; then
oven=$[ $oven + $i ]
else
odd=$[ $odd + $i ]
fi
done

echo "oven is $oven"
echo "odd is $odd"
  • 练习2:计算当前用户的id之和
#!/bin/bash
# 2:计算当前用户的id之和

declare -i idcount=0
# 取出/etc/passwd文件中用户的个数
userRow=$(wc -l /etc/passwd|cut -d" " -f1)

# 这里不能使用{},作为列表,用seq:for i in `seq 1 $userRow`; do
for i in $(seq 1 $userRow); do
userName=$(cut -d: -f1 /etc/passwd|sed -n "$i"p)
idcount=$[ $idcount + $(id -u $userName) ]
done

echo "$idcount"
  • 练习3:通过脚本参数传递一个目录,计算所有的文本文件的行数之和,并说明文件的总数
#/bin/bash
#3:通过脚本参数传递一个目录,计算所有的文本文件的行数之和,并说明文件的总数

if [[ $# < 1 ]];then
echo "A argument is needed."
exit 1
fi

declare -i fileCounts=0
declare -i rowsCounts=0

# 文件数
fileCounts=$( find $1 -type f | wc -l )

for i in `seq 1 $fileCounts`; do
#这里的egrep其实可以不要,也可以取出行数
rowCount=$(find $1 -type f -exec wc -l {} \; | cut -d" " -f1 | sed -n "$i"p)
rowsCounts=$[ $rowsCounts + $rowCount ]

done

echo "fileCounts is $fileCounts , rowsCounts is $rowsCounts"
  • 控制变量
for((i=1;i<10;i++));do
进入循环代码
done
  • (2)while循环 (重点必须掌握)
    示例:求100以内所有正整数之和
    • while CONDITION ; do
      循环体
      循环控制变量的修正表达式
      done
      进入条件:CONDITION测试为“真”
      退出条件:CONDITION测试为“假”
#!/bin/bash
#求100以内所有正整数之和

declare -i sum=0
declare -i i=1

while [ $i -le 100 ]; do
sum=$[ $sum+$i ]
let i++
done

echo "sum is $sum"
  • (3)until循环 (非重点了解即可)
    示例:求100以内所有正整数之和
    • until CONDITION ; do
      循环体
      循环控制变量
      done
      进入条件:CONDITION测试为“假”
      退出条件:CONDITION测试为“真”
i=1
sum=0

until [ $i -gt 100 ];do
sum=$[ $sum+$i ]
let i++
done


echo "sum to $sum"
  • 综合练习1:分别使用while for until实现,求100以内所有的偶数之和,100以内奇数之和
#!/bin/bash
# 分别求100以内所有的偶数之和,100以内奇数之和

declare -i oven=0
declare -i odd=0

for i in {1..100}; do
count=$[ $i%2 ]
if [ $count -eq 0 ]; then
oven=$[ $oven+$i ]
else
odd=$[ $odd+$i ]
fi
done

echo "odd is $odd"
echo "oven is $oven"
oven=0
odd=0
i=1
while [ $i -le 100 ]; do
count=$[ $i%2 ]
if [ $count -eq 0 ]; then
let oven+=$i
else
let odd+=$i
fi
let i++
done

echo "odd is $odd"
echo "oven is $oven"
oven=0
odd=0
i=1

until [ $i -gt 100 ]; do
count=$[ $i%2 ]
if [ $count -eq 0 ]; then
let oven+=$i
else
let odd+=$i
fi
let i++
done

echo "odd is $odd"
echo "oven is $oven"
  • 综合练习2:创建9个用户,user101-user109;密码同用户名, 如果这些用户存在就删除,不存在就创建
#!/bin/bash

for i in `seq 1 10`;do
if id user10"$i" &> /dev/null;then
userdel -r user10"$i"
else
useradd user10"$i"
echo "user10$i" | passwd --stdin user10"$i" &> /dev/null
echo "create user user10$i"
fi
done
  • 综合练习3:打印九九乘法表
#!/bin/bash
# 注意:这里的echo 的 -n选项表示不换行
for((i=1;i<10;i++));do
for((j=1;j<=$i;j++));do
echo -n "$j X $i = $[ $j*$i ] "
done
echo
done
  • 综合练习4:打印逆序的九九乘法表
#!/bin/bash
#打印逆序九九乘法表

declare -i k=9
while [ $k -ge 1 ]; do
for i in `seq 1 $k| sort -n -r`;do
echo -n "$i X $k = $[ $i*$k ] "
done
echo ""
let k--
done

(五)bash编程权威指南5

前言:continue、break、sleep命令、死循环,在循环语句中扮演了重要的角色,他们可以控制整个循环的执行过程
continue:跳出本次循环,进入下一轮循环
break:跳出整个循环
sleep:程序睡眠一个时间段
死循环:条件为true,永远为死循环

  • continue语句格式
while [ 条件1 ]; do
满足条件执行这里的代码
if [ 条件2 ]; then
# 跳出当前循环进入下一轮循环
continue
fi
满足条件执行这里的代码
done
  • 示例:求100以内所有的偶数之和
#/bin/bash
# 求100以内所有的偶数之和
# author

declare -i evensum=0

for i in {1..100}; do
if [ $[ $i%2 ] -eq 1 ];then
continue
fi

let evensum+=$i

done

echo "evensum is $evensum"

#/bin/bash
# 求100以内所有的偶数之和
# author
declare -i evensum=0
declare -i i=0

while [ $i -lt 100 ]; do

let i++
if [ $[ $i%2 ] -eq 1 ];then
continue
fi

let evensum+=$i


done

echo "evensum is $evensum"
  • break :直接跳出整个循环
while [条件1]; do
执行这里的代码
if [条件2]; then
break
fi
执行这里的代码
done

  • 死循环:如何创建死循环
while true;do
循环体
done
退出方式:某个测试条件满足的时候,让循环体执行break命令就是
  • 示例:计算100以内奇数之和
#/bin/bash
# description
# author
declare -i oddsum=0
declare -i i=1

while true; do
let oddsum=$oddsum+$i
let i=$i+2
if [ $i -gt 100 ];then
break
fi
done

echo "oddsum is $oddsum"
  • sleep命令
  • 示例:每隔3秒钟到系统上获取已经登入的用户的信息,其中,如果sb用户登入了系统,则给QQ发送邮件,主题为“sb user is login”,并退出
先修改/etc/mail.rc文件
set from=yinhuanyi_cn@163.com smtp=smtp.163.com
set smtp-auth-user=yinhuanyi_cn@163.com smtp-auth-password=yhy3426356
set smtp-auth=login
while true; do
sleep 3
if who | grep logstash &>/dev/null;then
echo "sb user is login" | mail -s 'somebody login' 307443272@qq.com
break
fi
done
  • while循环的特殊用法(遍历文件的行)
while read VARIABLE;do
循环体
done < /PATH/TO/FILE

意思是:依次读取/PATH/TO/FILE文件中的每一行,且将其赋值给VARIABLE变量
  • 示例:找出ID号为偶数的用户,显示其用户名、ID、及默认的shell
#/bin/bash
# 找出ID号为偶数的用户,显示其用户名、ID、及默认的shell
# author

while read line; do

ID=$(echo $line|cut -d: -f3)
if [ $[ $ID%2 ] -eq 0 ];then
username=$(echo $line | cut -d: -f1)
shell=$(echo $line | cut -d: -f7)
echo "username is $username, ID is $ID , shell is $shell"
fi
done < /etc/passwd
  • 实战示例:一直监听/data/目录下的文件变化,一旦变化就会把变化的信息赋值给file变量,那么对于file来说,while语句的循环体内并不需要拿到file的值,这里的while语句的功用在于只要file被赋值了,就会执行循环体内的代码。while内部的代码是无差异的数据同步。
#!/bin/bash
inotify=/usr/bin/inotifywait
while read file; do
/usr/bin/rsync -avz --delete /data/ nfs@192.168.23.26::data --password-file=/etc/rsync.password
done < $inotify -mrq --timefmt '%d/%m/%y %H:%M' --format '%T %w%f' -e modify,delete,create,attrib,close_write /data
  • for循环的特殊用法
for((控制变量初始化;条件判断表达式;控制变量修正语句));do
循环体
done

注意:控制变量初始化:仅仅在循环代码开始运行时执行一次
控制变量的修正语句:每轮循环结束会先进行控制变量修正运算,而后再做条件判断

示例:1到100的和

declare -i sum=0

for((i=1;i<=100;i++));do
let sum+=$i
done
echo "sum is $sum"


示例:打印九九乘法表
#/bin/bash
# description
# author

for((i=1;i<=9;i++));do
for((j=1;j<=i;j++));do
echo -ne "${j}X${i}=$[ ${j}*${i} ]\t"
done
echo
done
posted @ 2017-08-14 19:29  梦想总是遥不可及  阅读(904)  评论(0编辑  收藏  举报