shell编程练习题库1

一、说明

本文的shell编程题均是来自网上,平时作为练习时使用,没有任何顺序,随意写的

二、常见面试题

习题一:请编写一个脚本找出某个目录下超过50M的目录

#!/bin/bash

shopt -s -o nounset

DIR=${1:?'please enter the path which you want to check'}
if [[ ! $DIR == /* ]];then
    DIR=/$DIR
fi

declare -i size SIZE
SIZE=50

while read size dir
do
    if [ $size -gt $SIZE ];then
        echo -e "$size\t\t$dir"
    fi
done << (find $DIR -mindepth -type d -exec du -sm {} \;)

上面这个脚本的最后一句报错:
new 2.bash: line 18: syntax error near unexpected token (' new 2.bash: line 18: done << (find $DIR -mindepth -type d -exec du -sm {} ;)'
修改后的脚本1如下

#!/bin/bash

shopt -s -o nounset

DIR=${1:?'please enter the path which you want to check'}
if [[ ! $DIR == /* ]];then
    DIR=/$DIR
fi
>dir.txt
declare -i size SIZE
SIZE=5
du -sm $DIR/* > dir.txt
while read size dir
do
    if [ $size -gt $SIZE ];then
        echo -e "$size\t\t$dir"
    fi
done < dir.txt

修改后的脚本2如下

#!/bin/bash
# 找出某个目录下超过50M的目录
[ $# -ne 1 ] && { echo "Usage: $(basename $0) arguments";exit 1; }
Dir=$1
[[ ! $Dir == /* ]] && $Dir=/$Dir
>dir.txt
declare -i size SIZE
SIZE=5
du -sm $Dir/* > dir.txt
while read size dir
do
    [ $size -gt $SIZE ] && echo -e "$size\t\t$dir"
done <dir.txt

前面修改后的脚本还存在问题,比如执行bash tmp就会报错
修改后的脚本3

#!/bin/bash

prob1 () {
du -sm $Dir/* > dir.txt
while read size dir
do
    [ $size -gt $SIZE ] && echo -e "$size\t\t$dir"
done <dir.txt
}

prob2 () {
du -sm $a/* > dir.txt
while read size dir
do
    [ $size -gt $SIZE ] && echo -e "$size\t\t$dir"
done <dir.txt
}

[ $# -ne 1 ] && { echo "Usage: $(basename $0) arguments";exit 1; }
declare -i size SIZE
SIZE=50
Dir=$1
[[ ! $Dir == /* ]] && { a="/$Dir";prob2; } || { prob1; } 
>dir.txt   

习题二:使用for循环在/oldboy目录下通过随机小写10个字母加固定字符串oldboy批量创建10个html文件
脚本1:

#!/bin/bash
dir="/oldboy"
[ -d $dir ] || mkdir $dir

i=1
while ((i < 11))
do
    cd $dir && touch `tr -dc "a-z" < /dev/urandom | head -c 10`_oldboy.html
    i=$((i + 1))
done

脚本2:

#!/bin/bash
Dir="/oldboy"  
[ ! -d ${Dir} ] && mkdir /oldboy || cd $Dir
for (( i=1;i<=10;i++ ))
do
    touch `tr -dc "a-z" < /dev/urandom | head -c 10`_oldboy.html
done

习题三、将上题中的oldboy目录下的文件名改为oldgirl,并且html改成大写

#!/bin/bash

a=$(ls -l  "/oldboy" | awk '{print $9}' | awk -F '_' '{print $1}' | egrep -v '#|^$')
b=($a)
cd /oldboy
for i in  ${b[@]}
do
    mv ${i}_oldboy.html ${i}_oldgirl.HTML
done

习题四、批量创建10个系统帐号oldboy01-oldboy10并设置密码(密码为随机8位字符串)

#!/bin/bash

for i in $(seq -w 11 15)
do
    useradd oldboy$i
    PASSWD=$(tr -dc [:alpha:] < /dev/urandom |head -c 8)
    echo $PASSWD | passwd --stdin "oldboy$i"
done

习题五、写一个脚本,实现判断172.168.1.0/24网络里,当前在线用户的IP有哪些
脚本1:

#!/bin/bash

net="172.168.1.0"
NET=${net:0:10}
echo "在线的机器有" >> online.txt
echo "不在线的机器有" >> offline.txt
for i in {1..255}
do
    ping -c 1 ${NET}${i}
    if [ $? -eq 0 ];then
        echo "${NET}${i}" >> online.txt
    else
        echo "${NET}${i}" >> offline.txt
    fi
done
cat offline.txt >> online.txt
rm -f offline.txt
cat online.txt

脚本2:

# 判断网络10.8.8.0/24里在线的机器有哪些
echo "在线机器" > online.txt
echo "不在线的机器" > offline.txt
for i in $(seq 1 254)
do
    ping -c 1 10.8.8.$i
    [ $? -eq 0 ] && echo 10.8.8.$i >> online.txt || echo 10.8.8.$i >> offline.txt
done
# 一般来说在线的机器少与不在线的机器,因为要用到paste命令,所以这里我们使用\t来补足在线机器统计文件里不足的行
# 这样可以使得到的信息更易查看
num1=$(cat online.txt | wc -l)
num2=$(($num1 - 1))
num3=$((254 - $num2 - $num2))
for ((i=1;i<=$num3;i++))
do
    echo -e "\t" >> online.txt
done
paste online.txt offline.txt > txt

习题六、实现对MySQL数据库进行分库备份
脚本1:

#!/bin/bash

# 分库备份
db1="fishomics"
db2="genomedb"
username="***"
password="***"
a=($db1 $db2)
[ -d /data/bak/ ] || mkdir -p /data/bak/
mkdir -p /data/bak/fishomics
mkdir -p /data/bak/genomedb
for i in ${a[@]}
do
    docker exec  mysql_gene mysqldump -u$username -p$password --databases $i > /data/bak/${i}/${i}_`date +%Y%m%d`.sql
done

脚本2:

#!/bin/bash

# 本地分库备份非系统数据库,数据库版本MySQL 5.7
# 变量设定
container="mysql_linuxwt"
user="root"
password="sinobridge"
port=33066

# 获取备份库名
dbname=$(docker exec ${container} mysql -h localhost -u ${user} -p${password} -P${port} -e "show databases;" | \
grep -v Database | grep -v information_schema | grep -v sys | grep -v mysql | grep -v performance_schema)
# 备份
for db in ${dbname[@]}
do
    docker exec ${container} mysqldump -h localhost -u ${user} -p${password} -P${port} --databases $db > /root/bak/${db}_$(date +%Y%m%d_%R).sql
done

习题七、实现对mysql数据库进行分表备份

#!/bin/bash

ip="172.168.1.26"
username="***"
password="***"
port="33066"

db=(fishonics genome)

for dbname in ${db[@]}
do
    [ -d /data/bak/$dbname ] || mkdir -p /data/bak/$dbname
    table_list=$(mysql -u$username -p$password -h $ip -P $port -e "show databases;use $dbname;show tables;" | grep -v Tables_in_$dbname)
    
    for table in ${table_list[@]}
    do
        mysqldump -u$username -p$password  -h $ip $dbname ${table}  > /data/bak/$dbname/${table}`date +%Y%m%d_%H%m%s`sql
    done
done

习题八、获取本机IP

#!/bin/bash

a=$(ls -l /etc/sysconfig/network-scripts/ | grep ifcfg | grep -v lo | awk  '{print $9}' | awk -F '-' '{print $2}')
IP=$(ip addr |grep $a | grep inet | awk '{print $2}' | awk -F '/' '{print $1}')
echo $IP

习题九、打印99乘法表
一次面试让写一个打印99乘法表的脚本,不会真是尴尬,大好机会错失
写法一:

#!/bin/bash

for (( i=1; i<=9; i++ ))
do
    for (j=1; j<=9; j++ ))
    do
        [ $j -le $i ] && echo -n "$i*$j=$[$i*$j] "
    done
    echo ""
done    

写法二:

#!/bin/bash

for ((i=1;i<10;i++))
do
    for ((j=1;j<=i;j++))
    do
        let "p=${i}*${j}"
        echo -n "$i*$j=$p "
    done
    echo
done

习题十、远程到多台机器上执行操作

#!/bin/bash

for p in $(seq 1 10)
do
ssh username@10.8.8.${p} <<EOF
a=$(find /root -mmin -30 -name '*txt')
for b in ${a[@]}
do
echo $b
done
EOF
done

习题十一、打印下面这句话中字母数不大于6的单词
I am oldboy teacher welcome to oldboy training class

#!/bin/bash

words=(I am oldboy teacher welcome to oldboy training class)
for word in ${words[@]}
do
    len=$(echo ${word} | wc -c)
    if ((len <= 6));then
        echo ${word}
    fi
done

习题十二、开发shell脚本分别实现以脚本传参以及read读入的方式比较2个整数大小。以屏幕输出的方式提醒用户比较结果。注意:一共是开发2个脚本。当用脚本传参以及read读入的方式需要对变量是否为数字、并且传参个数做判断
脚本1:

#!/bin/bash

if [ $# -ne 2 ];then
    echo "args is error,yo need two args"
    exit 1
else
    if [[ $1 =~ ^[0-9]$ && $2 =~ ^[0-9]$ ]];then
        if [ $1 -gt $2 ];then
            echo "$1大于$2"
        elif [ $1 -lt $2 ];then
            echo "$1小于$2"
        else
            echo "$1等于$2"
        fi
    else
        echo "invalid entry"
        exit 1
    fi
fi

脚本2:

#!/bin/bash

echo "please enter two integer->" 
read a b
if [[ $a =~ ^[0-9]$ && $b =~ ^[0-9]$ ]];then
    if [ $a -gt $b ];then
        echo "$a比$b大"
    elif [ $a -lt $b ];then
        echo "$a比$b小"
    else 
        echo "$a等于$b"
    fi

习题十三、打印选择菜单,一键安装web服务

#!/bin/bash

DELAY=3
while [[ $REPLY != 0 ]]
do
    clear
cat <<EOF
please select:
1、install http
2、quit
EOF
    read -p "select [1-2]>"
    if [[ $REPLY =~ ^[1-2]$ ]];then
        if [ $REPLY -eq 1 ];then
            yum -y install httpd
            sleep $DELAY
        fi
        if [ $REPLY -eq 2 ];then
            exit
        fi
    else
        echo "invalid entry."
        exit 1
    fi
done
echo "program terminated"

习题十四、监控nginx服务是否正常,试用三种策略来实现
大致有三种思路:
监控进程个数
监控pid号
监控端口号
这里着重说一下监控端口号
netstat -ntlp|grep 80 | awk '{print $4}' | awk -F ':' '{print $4}' | grep '^80$'
上面这条命令可以过滤出80端口
习题十五、写一个脚本解决DOS攻击
具体思路为:通过web日志过滤出在短时间内某个ip访问服务器的PV数达到某个数值时禁用掉该Ip,并定期进行检查
提示命令:firewall-cmd --permanent --zone=public --add-rich-rule="rule family="ipv4" source address="ip" port protocol="tcp" port="80" reject"

#!/bin/bash

# shell来封掉3分钟内pv树超过100的ip
while ((1))
do
    a=$(netstat -an|grep ESTAB | awk '{print $5}' | awk -F ':' '{print $1}' | sort | uniq -c)
    >log
    echo $a >> log
    while read line
    do
        pv=$(echo $line | awk '{print $1}')
        ip=$(echo $line | awk '{print $2}')
        prog=$(firewall-cmd --list-all | grep $ip | wc -l)
        if [[ $pv -gt 100 && $prog -eq 0 ]];then
            firewall-cmd --zone=public --permanent --add-rich-rule="rule family="ipv4" source address="$ip" port protocol="tcp" port="80" reject"
        fi
    done << log
    sleep 180
done

上面的if循环后面的判断是判断过滤出来的ip是否在我们的防火墙允许规则之内,如果是在允许的就不能禁用,再就是如果防火墙使用的是iptables,if判断应该写成

prog=$(iptables -L -n | grep $ip | wc -l)  
if [[ $pv -gt 100 && $prog -eq 0 ]];then  
    iptables -I input -s $ip -j drop   
fi  

习题十六、以rsync为例来编写系统启动脚本
cat rsync.sh

#!/bin/bash

# 有时候我们在局域网内没有办法通过yum来安装最新的rsync,甚至无法上外网,我们需要去手动上传其源码包到服务器上
# 安装,这个时候就为了方便往往需要去写一个系统启动脚本,该脚本可作为其他第三方软件部署后的一个示例,但是该方
# 式是一种老的写法,常用于centos6和5,centos7也可以使用这种方式,因为它保留了chkconfig的功能

[ -f /etc/init.d/functions ] && . /etc/init.d/fucntions || exit 2
start () {
    if [ $(lsof -i:873 | wc -l) -gt 0 ];then
        echo "rsyncd is running..."
    else
        /usr/local/rsync/bin/rsync --daemon
        [ `lsof -i:873|wc -l` -gt 0 ]&&action "rsync start..." /bin/true
    fi
    return $RETVAL
}
stop () {
    if [ $(lsof -i:873 | wc -l) -eq 0 ];then
        echo "rsyncd is not running..."
    else
        killall rsync
        sleep 3
        [ `lsof -i:873|wc -l` -eq 0 ]&&action "rsync stop..." /bin/true
    fi
    return $RETVAL
}
main () {
    case $1 in
    start|START)
          start
           ;;
    stop|STOP)
          stop
           ;;
    restart|RESTART)
          stop
          sleep 2
          start
           ;;
    *)
           echo -e "Usage: $0 {start|stop|restart}"
           exit 1
           ;;
    esac
}
main $1
exit $RETVAL

习题十七、三个学生参与抓阄来决定谁去参加实践活动,具体要求如下:
1、执行脚本后,想去的同学输入英文名字全拼,产生随机数01-99之间的数字,数字越大就去参加实践,前面抓到的数字,下次不能出现相同的数字
2、第一个输入名字后,屏幕输出信息,并将名字和数字记录到文件里,程序不能退出继续等待别的人输入
我自己写的一个实现部分功能的脚本
cat shijian.sh

#!/bin/bash

>name.log
>shijian.txt
DELAY=3
while [[ $REPLY != 0 ]]
do
    clear
cat <<EOF
1、please enter your English name.
2、比较号的大小,号大的参加实践
3、quit
EOF
    
    read -p "select [1-3]>"
    if [[ $REPLY == 1 ]];then
        read NAME
        num=$(tr -dc "0-9" < /dev/urandom | head -c 2)
        echo "$NAME: your num is $num"
        echo -e "$NAME $num" >> name.log
        sleep $DELAY
    elif [[ $REPLY == 2 ]];then
        a=$(cat name.log)
        echo "$a" >> shijian.txt
        echo ""   >> shijian.txt
        m=$(cat shijian.txt | grep -v '^$')
        echo $m >shijian.txt
        b=$(sort -t " " -k2 -n shijian.txt)
        c=$(echo $b|awk '{print $1}' | tail -n 1)
        echo "$c可以参加实践"
        sleep $DELAY
    else
        echo "invalidary entry."
        exit 1
    fi
done

别人写的一个脚本
cat pp.sh

#!/bin/bash

Name () {
    while true
    do
        read -p "input your name:" a
        [ ! -z "$a" ] && break
    done
}

random_num () {
    for ((i=1;i<=5;i++))
    do
        Name
        num=$(echo $RANDOM|cut -c 1-2)
        [ $(cat random_num.txt|grep $num | wc -l) -gt 0 ] && result=1
        [[ $result == 1 ]] && continue
        echo "your name is $a,num is $num"
        echo "$a $num" >> random_num.txt
    done
}
main () {
    echo "Name num" > random_num.txt
    random_num
}
main

习题十八、批量检查多个网站地址是否正常
要求:shell数组方法实现,检测策略尽量模拟用户访问思路
http://www.etiantian.org
http://www.taobao.com
http://oldboy.blog.51cto.com
http://10.0.0.7
cat webcheck.sh

#!/bin/bash

[ -f /usr/bin/wget ] || yum -y install wget
[ -f /usr/bin/curl ] || yum -y install curl
a="http://www.etiantian.com"
b="http://www.taobao.com"
c="http://oldboy.blog.51cto.com"
d="http://10.0.0.7"
>state.txt
webs=($a $b $c $d)
for web in ${webs[@]}
do
wget $web > wget.txt 2>&1
wget_num=$(cat wget.txt | grep "HTTP request" | grep 200 | wc -l)
if [[ $wget_num == 1 ]];then
    echo "The website $web is normal." >> state.txt
else
    echo "The website $web is unnormal" >> state.txt
fi
done   
cat state.txt

也可以使用curl命令来检测curl -I -s 地址 | awk 'NR==1 {print $2}'

习题十九、已知下面的字符串是通过RANDOM随机数变量md5sum|cut-c 1-8截取后的结果,请破解这些字符串对应的md5sum前的RANDOM对应数字
21029299 00205d1c a3da1677 1f6d12dd 890684b
cat random.sh

#!/bin/bash

for i in $(seq 1 65535)
do
    a=$(echo $i | md5sum | cut -c 1-8)
    echo $i $a >> md5.txt
done

char=(21029299 00205d1c a3da1677 1f6d12dd 890684b)
for j in ${char[@]}
do
    grep -h $j md5.txt
done

习题二十、检查目录/var/www/html下的文件是否被篡改,输出被改的文件名
cat file_check.sh


dosomething () {
    a=$(grep FAILED /tmp/md5.check | awk -F ':' '{print $1}')
    echo -e "$a is changed ."    
}

md5_file () {
    find /var/www/html -type f | xargs md5sum > /tmp/html.md5
}

state () {
    echo "not change"
}

[ -f /tmp/html.md5 ] || md5_file
md5sum -c /tmp/html.md5 > /tmp/md5.check 2>&1
[ $? -eq 0 ] && state || dosomething

二十一、用shell处理以下内容,并实现以下需求
the squid project provides a number of resources toassist users design,implement and support squid installations. Please browsethe documentation and support sections for more infomation
1、按单词出现频率降序排序!
2、按字母出现频率降序排序!
我自己写的脚本
cat paixu.sh

#!/bin/bash

# 单词出现频率降序排列
echo "单词降序排列"
>word.txt
echo "the squid project provides a number of resources toassist users design,implement and support squid installations. Please browsethe documentation and support sections for more infomation" > test.txt
words=$(strings test.txt)
for word in ${words[@]}
do
    echo $word>> word.txt
done
cat word.txt | sort | uniq -c > paixu.txt
sort -nr paixu.txt

# 字母出现频率降序排列
echo "字母降序排列"
>char.txt
char="the squid project provides a number of resources toassist users design,implement and support squid installations. Please browsethe documentation and support sections for more infomation"
# 消除空格
charc=${char// /}
charp=${charc//,/}
charq=${charp//./}
num=$(echo $charq | wc -c)
numq=$((num - 1))
for i in $(seq 0 $numq)
do
a=${charq:$i:1}
echo $a >> char.txt
done
cat char.txt | sort | uniq -c > paixu2.txt
sort  -nr paixu2.txt

别人的脚本
cat paixu2.sh

#!/bin/bash

# 单词降序排列
echo "单词降序排列"
>haha.txt
echo "the squid project provides a number of resources toassist users design,implement and support squid installations. Please browsethe documentation and support sections for more infomatio" > test
#a=(the squid project provides a number of resources toassist users design,implement and support squid installations. Please browsethe documentation and support sections for more infomatio)

sed -i 's/\,\|\./ /g' test 
a=$(cat test)
for i in ${a[@]}
do
    echo $i >> haha.txt
done
cat haha.txt | sort | uniq -c  > jj.txt
sort -nr  jj.txt 

# 字母降序排列
echo "字母降序排列"
>haha2.txt
for i in ${a[@]}
do
    for j in ${i[@]} 
    do
        echo $j | awk -F '' '{for(p=1;p<=NF;p++)print $p}' >>haha2.txt
    done
done
cat haha2.txt | sort | uniq -c  > qq.txt
sort -nr  qq.txt

比较一下发现自己写的脚本太冗杂了,结构也不清晰
习题二十二、输出正方形、等腰三角形和直角梯形
cat xin.sh

#!/bin/bash

echo "正方形"
>zf.txt
for (( i=1; i<=10; ++i ))
do
    echo "* * * * * * * * * *"  >> zf.txt
done
sed -i '2,9s/\* \* \* \* \* \* \* \* \* \*/\*                 \*/g' zf.txt
cat zf.txt
echo "等腰三角形"
>dy.txt
    echo "    *" >> dy.txt
    echo "   * *" >> dy.txt
    echo "  * * *" >> dy.txt
    echo " * * * *" >> dy.txt
    echo "* * * * *" >> dy.txt
sed -i '3s/\* \* \*/\*   \*/g' dy.txt
sed -i '4s/\* \* \* \*/\*     \*/g' dy.txt 
cat dy.txt
echo "直角梯形"
>zt.txt
echo "* * * * *" >> zt.txt
echo "* * * * * *" >> zt.txt
echo "* * * * * * *" >> zt.txt
echo "* * * * * * * *" >> zt.txt
echo "* * * * * * * * *" >> zt.txt
echo "* * * * * * * * * *" >> zt.txt
sed -i '2s/\* \* \* \* \* \*/\*         \* /g' zt.txt 
sed -i '3s/\* \* \* \* \* \* \*/\*           \*/g' zt.txt
sed -i '4s/\* \* \* \* \* \* \* \*/\*             \*/g' zt.txt 
sed -i '5s/\* \* \* \* \* \* \* \* \*/\*               \*/g' zt.txt
cat zt.txt

习题二十三、结合页面来实现监控nginx的状态,效果图如下
04101010
cat nginx_check.sh

ip_tool=(
172.168.1.29:74
172.168.1.29:94
172.168.1.30
)
file_location=/data/gooalgene/nginx/nginx_web/nginx_debian/html/index.html
web_result () {
rs=$(curl -I -s $1 | awk 'NR==1{print $2}')
return $rs
}
new_row () {
cat <<EOF>>$file_location
<tr>
<td bgcolor="$4">$1</td>
<td bgcolor="$4">$2</td>
<td bgcolor="$4">$3</td>
</tr>
EOF
}
auto_html () {
web_result $2
rs=$?
[ $rs -eq 200 ] && new_row $1 $2 up green || new_row $1 $2 down red
}
main () {
while  ((1))
do
    cat <<EOF>>$file_location
<h4>the Status of RS:</h4>
<meta http-equiv="refresh" content="1">
<table border="1">
<tr>
<th>NO:</th>
<th>IP:</th>
<th>Status</th>
</tr>  
    for (( i=0; i<${#ip_tool[*]}; i++ ))
    do
        auto_html $i ${ip_tool[$i]}
    done
    cat <<EOF>>$file_location
</table>
EOF
sleep 5
>$file_location
done
}
main

该脚本结合了html的基础知识来展示nginx的检查结果
习题二十四、以输入的数字打印出相应长度的正方形
cat dy.sh

#!/bin/bash

DELAY=3

while [[ $REPLY != 0 ]]
do
    clear
    cat <<EOF
0、quit
1、打印正方形
EOF
    read -p "select [0-1]>"
    if [[ $REPLY =~ ^[0-1]$ ]];then
        if [[ $REPLY == 1 ]];then
            echo -n "enter the lenth of 正方形->"
            read n
            b=$((n + 1))
            a=$(seq -s '*' $b)
            for (( i=1; i<=n; i++ ))
            do
                echo $a | sed 's/[0-9]/ /g'  >>dy.txt
            done
            sed -i 's/^[ \t]*//g' dy.txt
            cat dy.txt
            sleep $DELAY
        fi
    else
        echo "invalid entry"
        sleep $DELAY
    fi     
    >dy.txt   
done
echo "progname terminated"

习题二十六、以输入的数字为腰长来打印一个等腰三角形
我自己写的一个脚本(该脚本只适合长度为2-9的三角形)
cat dyzj.sh

#!/bin/bash

DELAY=3
while [[ $REPLY != 0 ]]
do
clear
    cat <<EOF
1、quit
2、输出等腰三角形
EOF
    read -p "select [1-2]>"
    if [[ $REPLY =~ ^[1-2]$ ]];then
        if [[ $REPLY == 1 ]];then
            echo "enter the 底长"
            read n
            m=$((n + 1))
            for (( i=1; i<=m; i++ ))
            do
                seq -s '*' $i >> dy.txt
            done
            sed -i "s/[0-9]/ /g" dy.txt
            sed -i "s/^[ \t]//g" dy.txt # 去掉每行开头二点空格或者tab
            sed -i '1d' dy.txt
            cat dy.txt
            sleep $DELAY
        else
            exit 1
        fi
    else
        echo "invalid entry"
        sleep $DELAY
    fi 
    >dy.txt
done
echo "progname terminated."

别人写的脚本(可以实现任意长度的等腰直角三角形)
cat dyzj.sh

read -p "enter the num:" a   
for (( i=1; i<=$a; i++ ))
do
    for (( h=$((2*$a-2*$i)); h>=0; h-- ))
    do
        echo -e " \c"
    done
    for (( j=1; j<=$((2*$i-1)); j++ ))
    do
        echo -e "*\c"
    done
    echo ""
done  

注意:一定要改变编程思维,同样的事情肯定是需要去进行循环处理,特别是循环嵌套处理
习题二十七、以输入的数字m和n来输出直角梯形
cat txzj.sh

#!/bin/bash

# n a 表示以2n-1为上底,2a-1为下底,a-n为高
read -p "Please Enter a number:" n a


for ((i=n;$i<=$a;i++))
do
     for ((h=$((2*$a-2*$i));h>=0;h--))
     do
         echo -e " \c"
     done
     for ((j=1;j<=$((2*$i-1));j++))
     do
         echo -n "*"  #不换行
     done
     echo 
done

三、基础练习题

习题一、显示文件脚本名称
cat name.sh

#!/bin/bash

echo $0

习题二、利用位置参数实现文件的移动
cat mv.sh

#!/bin/bash

mv $1 $2

执行脚本
bash mv.sh file1 file2
习题三、计算1-100的和
法一:

#!/bin/bash

sum=0
for ((i=1;i<=100;i++))
do
   sum=$((sum + $i))
done
echo $sum

法二:

#!/bin/bash

sum=0
i=1
while ((i<=100))
do
    sum=$((sum + i))
    ((i++))
done
echo $sum

习题四、输入一个数字n并计算1到这个数字的和,并同时满足下面的要求,如果这个数小于等于1,要求重新输入大于1的数字为止

#!/bin/bash

caculate () {
    sum=0
    for ((i=1;i<=n;i++))
    do
        sum=$((sum + i))
    done
    echo $sum
}

enter1 () {
    read -p "enter a  num:" n
}

enter2 () {
    read -p "enter a integar which is big than 1:" n
} 

bijiao () {
    while ((n<=1))
    do
        enter2
    done
}

enter1

n1=$(echo $n | sed "s/[0-9]//g")

[ -z "$n1" ] && { bijiao ;caculate;} || { echo "enter invalid entry.";exit 1;}

习题五、编写脚本把/root下的所有目录都复制到/tmp下

#!/bin/bash

for i in $(ls /root)
do
    [ -d "$i" ] && cp -r $i /tmp || { echo "$i is not dir." >&2;exit 1}
done

习题六、给有登陆权限的用户打招呼,比如hello,王腾你的uid号是1000

#!/bin/bash

cat /etc/passwd | grep -v nologin | awk -F ':' '{print $1}' >user.txt
cat /etc/passwd | grep -v nologin | awk -F ':' '{print $3}' >uid.txt
paste -d " " user.txt uid.txt

while read user uid
do
    echo "Hello,${user},your id is ${uid}."
done < user_uid.txt

习题七、将当前目录大于5M的文件移动到/tmp
先写一个实现基本实现功能的脚本
cat mvfile.sh

#!/bin/bash

# mv the file which is bigger than 10M in current dir to /tmp

# judge whether exist position arguments

# get the current dir
current_dir=$(pwd)

# create a null  file which storage name and size of the suitable file

>file.txt

declare -i size SIZE

SIZE=5


# put the file name and size into the null file
du -sm ${current_dir}/* > file.txt

# use while loop
while read size dir
do
    [ ${size} -gt ${SIZE} ] && cp -r ${dir} /tmp
done < file.txt

通用脚本1

#!/bin/bash

# judge whether is root 
ROOT_UID=0
E_NOROOT=67
[ ${UID} -ne 0 ] && { echo "Must be root to run the script.";exit ${E_NOROOT};}

# DIR=${1:-$(pwd)}
# judge whether exist position args
E_ERROTARG=65
case "$1" in
"") DIR=$(pwd);;
*[!0-9]*) DIR=$1;;
*) exit ${E_ERROEARG};;
esac

>file.txt
declare -i size SIZE
SIZE=5

du -sm $DIR/* > file.txt
while read size dir
do
    [ $size -gt $SIZE ] && cp -r $dir /tmp
done < file.txt

无位置参数默认查询当前目录,有位置参数查询位置参数的目录
通用脚本2

#!/bin/bash

# judge whether is root 
ROOT_UID=0
E_NOROOT=67
[ ${UID} -ne 0 ] && { echo "Must be root to run the script.";exit ${E_NOROOT};}

DIR=${1:-$(pwd)}
# judge whether exist position args
#E_ERROTARG=65
#case "$1" in
#"") DIR=$(pwd);;
#*[!0-9]*) DIR=$1;;
#*) exit ${E_ERROEARG};;
#esac

>file.txt
declare -i size SIZE
SIZE=5

du -sm $DIR/* > file.txt
while read size dir
do
    [ $size -gt $SIZE ] && cp -r $dir /tmp
done < file.txt

该脚本较通用脚本1更为简炼,性能更高,运用了空变量展开的特性
习题八、判断文件是否是字符文件,是就将其拷贝到/dev目录下

#!/bin/bash

UID_ROOT=0
[ ${UID} -ne ${UID_ROOT} ] && { echo "this script need root.";exit 1; }
file=${1:?please enter a file}
[ -c $file ] && cp -ap $file /tmp || { echo "the file is not a char file.";exit 1; }

习题九、请为下列程序添加注释,并说明程序的调用和方法

#!/bin/bash

# /etc/rc.d/rc.httpd
# start/stop/restart the apache web server
case "$1" in
start)
/usr/sbin/apachectl start
;;
stop)
/usr/sbin/apachectl stop
;;
restart)
/usr/sbin/apachectl restart
;;
*)
echo -e "Usage: $0 {start|stop|restart}"
exit 1
;;
esac

简易系统自启动脚本
习题十、添加一个新组class1,然后添加属于这个组的30个用户,用户名的形式为stdxx,其中xx为01到30

#!/bin/bash

#!/bin/bash

groupadd class1
for i in $(seq -w 1 30)
do
    useradd std$i
    echo "123"  | passwd --stdin "std$i"
done

习题十一、批量删除上面的30个用户

#!/bin/bash

for i in $(seq -w 1 30)
do
    userdel -r  std$i
done   

习题十二、某系统管理员需要每天做一定重复的工作,请按照以下需求制定一个解决方案:
1、在下午4:50删除/abc目录下的全部子目录和全部文件
2、从早上8:00到下午6:00每小时读取/xyz下x1文件中每一行第一个域的全部数据加入到/backup下的bak01.txt文件内
3、每逢星期一下午5:50将/data下的所有目录和文件归档并压缩为文件backup.tar.gz
4、在下午5:55将IDE的接口CD-ROM卸载,这里假设CD-ROM的设备名为/dev/hdc
5、在早上8点前开机后启动
思路,先写满足需求的脚本,然后将这些脚本添加到计划任务中
cat cron1.sh

#!/bin/bash

ROOT_UID=0
[ ${UID} -ne 0 ] && { echo "this scripte need root.";exit 1;}  
dir=${1:-"/test"}
[ -d "$dir" ] && { cd $dir;rm -Rf *; } || exit 1

cat cron2.sh


#!/bin/bash

ROOT_UID=0
[ ${UID} -ne 0 ] && { echo "this scripte need root.";exit 1;}
file=${1:-"/xyz/x1"}
[ -f $file ] &&  cat $file | awk '{print1}' > /backup/bak01.txt || { echo "there is no file $file";exit 1; }

cat cron3.sh


#!/bin/bash

ROOT_UID=0
[ ${UID} -ne 0 ] && { echo "this scripte need root.";exit 1;}
dir=${1:-"/data"}
[ -d $dir ] && { cd $dir;tar zvxf bakup.tar.gz ./*; } || { echo "there is no dir $dir.";exit 1; }

cat cron4.sh


#!/bin/bash

ROOT_UID=0
[ ${UID} -ne 0 ] && { echo "this scripte need root.";exit 1;}
device=${1:-"/dev/hdc"}
[ -b $device ] &&  umount /dev/hdc || { echo "there is no $device.";exit 1; }

下面写一个脚本将以上四个脚本添加到计划任务
cat cron.sh

#!/bin/bash

cat <<EOF>> /etc/crontab
50 4 * * *  root /root/cron1.sh
0 8-18/1 * * * root /root/cron2.sh
50 17 * * 1 root /root/cron3.sh
55 17 * * * root /root/cron4.sh
EOF

习题十三、设计一个shell程序,每个月的第一天备份并压缩/etc的所有内容存放在目录/root/bak里,文件名形式为yymmdd_etc,shell程序放在/usr/bin下面
cat fileback.sh

#!/bin/bash

# judge the user of the script
[ $[UID] -ne 0 ] && { echo "this script need root.";exit 1; }

etczip () {
    tar zvcf $(date "+%Y%m%d")_etc.tar.gz  /etc > /dev/null 2>&1
}

error () {
yum -y install gzip && mkdir -p /root/bak
etczip
}

[[ -f /usr/bin/gzip  && -d /root/bak ]]  &&  etczip  || error

mv $(basename $0) /usr/bin && mv *.tar.gz /root/bak

添加计划任务
* * 1 * * /usr/bin/fileback.sh