shell练习题

第一部分:基础概念与变量

1. Shell特殊位置参数变量的含义

变量含义
$0脚本本身的名称(如果以路径执行,则包含路径)
$n传递给脚本的第 n 个参数(n=1..9,${10} 表示第10个)
$#传递给脚本的参数总个数
$*所有参数作为一个整体字符串("$1 $2 ...")
$@所有参数作为独立字符串("$1" "$2" ...),推荐使用
$?上一条命令的退出状态码,0 表示成功,非0 失败
$$当前 shell 进程的 PID
$!后台运行的最后一个进程的 PID
$_上一个命令的最后一个参数

2. 如何定义环境变量

使用 export 命令将普通变量提升为环境变量(全局变量),例如:

export MYENV="hello"

若希望永久生效,可将上述命令写入 ~/.bashrc/etc/profile,然后执行 source ~/.bashrc

3. 变量定义时,不加引号、单引号、双引号、反引号的区别与用法

写法说明示例
不加引号值不能包含空格或特殊字符(如 &;),会进行变量替换和命令替换name=oldboy
单引号 ' '所见即所得,不解析变量和命令,所有字符均为字面量name='$a' → 值 $a
双引号 " "解析变量和命令替换,支持转义(如 \$name="$a" → 值等于变量 a 的值
反引号 ` `命令替换,执行里面的命令,将输出作为值(已过时,推荐 $()dir=`pwd`

用法建议

  • 普通无特殊字符的字符串:不加引号
  • 需要保留空格或特殊字符的字符串:加双引号
  • 不希望解析变量的内容:加单引号
  • 命令替换:优先使用 $(),例如 dir=$(pwd)

4. 特殊变量扩展的意义

表达式含义
${oldboy}取变量 oldboy 的值(与 $oldboy 相同,但花括号可界定变量名边界)
${#oldboy}取变量 oldboy 的字符串长度
${url#*.}url 变量的值开头删除最短匹配 *. 的部分(非贪婪,删除第一个点及其之前的内容)
${url##*.}从开头删除最长匹配 *. 的部分(贪婪,删除最后一个点及其之前的内容)
${url%.*}从末尾删除最短匹配 .* 的部分(非贪婪,删除最后一个点及其之后的内容)
${url%%.*}从末尾删除最长匹配 .* 的部分(贪婪,删除第一个点及其之后的内容)

举例url=www.sina.com

  • ${url#*.}sina.com
  • ${url##*.}com
  • ${url%.*}www.sina
  • ${url%%.*}www

5. 数值的整数和小数运算有哪几种方式

类型方式示例
整数运算exprexpr 1 + 1
$[]echo $[1+1]
$(())echo $((1+1))
letlet a=1+1
整数+小数运算bc 命令echo scale=2; 10/3" | bc
awkawk 'BEGIN{print 10/3}'

6. 条件测试的含义

表达式含义
[ -e dir|file ]测试文件或目录是否存在(exist)
[ -d dir ]测试是否为目录(directory)
[ -f file ]测试是否为普通文件(file)
[ -r file ]测试文件是否可读(read)
[ -x file ]测试文件是否可执行(executable)
[ -w file ]测试文件是否可写(write)

7. 字符串测试操作符

  • -n:字符串长度不为0则为真
  • -z:字符串长度为0则为真

8. 整数比较符

  • -eq:等于
  • -ne:不等于
  • -gt:大于
  • -ge:大于等于
  • -lt:小于
  • -le:小于等于

第二部分:简单编程题

9. 判断文件不存在则创建

[ -f /tmp/oldboy.txt ] || touch /tmp/oldboy.txt

10. 查看磁盘使用率,超过80%输出到/var/log/disk.err

#!/bin/bash
usage=$(df -h / | awk 'NR==2 {print $5}' | tr -d '%')
if [ $usage -gt 80 ]; then
    echo "$(date) 磁盘使用率 ${usage}% 超过80%" >> /var/log/disk.err
fi

11. 批量创建10个用户,密码123,判断输入为空和数字

#!/bin/bash
read -p "请输入用户名前缀: " prefix
read -p "请输入用户个数: " num
if [ -z "$prefix" ]; then
    echo "前缀不能为空"
    exit 1
fi
if ! [[ "$num" =~ ^[0-9]+$ ]]; then
    echo "个数必须是数字"
    exit 1
fi
for i in $(seq 1 $num); do
    username="${prefix}${i}"
    useradd $username 2>/dev/null
    echo "123" | passwd --stdin $username &>/dev/null
    echo "用户 $username 创建成功,密码123"
done

12. 猜数字游戏(1-100),统计次数

#!/bin/bash
target=$((RANDOM % 100 + 1))
count=0
while true; do
    read -p "请输入1-100之间的数字: " guess
    if ! [[ "$guess" =~ ^[0-9]+$ ]] || [ $guess -lt 1 ] || [ $guess -gt 100 ]; then
        echo "输入无效,请输入1-100的整数"
        continue
    fi
    ((count++))
    if [ $guess -gt $target ]; then
        echo "大了"
    elif [ $guess -lt $target ]; then
        echo "小了"
    else
        echo "恭喜你猜对了!共猜了 $count 次"
        break
    fi
done

13. 菜单循环+case

#!/bin/bash
while true; do
    cat << EOF
h 显示命令帮助
f 显示登陆信息
d 显示磁盘挂载
m 查看内存使用
u 查看系统负载
q 退出程序
EOF
    read -p "请输入选项: " opt
    case $opt in
        h) echo "帮助信息:可选 h,f,d,m,u,q" ;;
        f) who ;;
        d) df -h ;;
        m) free -h ;;
        u) uptime ;;
        q) exit 0 ;;
        *) echo "无效选项" ;;
    esac
done

14. nginx 启动脚本(case语句)

#!/bin/bash
nginx="/usr/sbin/nginx"
case $1 in
    start)
        $nginx
        ;;
    stop)
        $nginx -s stop
        ;;
    reload)
        $nginx -s reload
        ;;
    restart)
        $nginx -s stop && sleep 1 && $nginx
        ;;
    status)
        if pgrep nginx >/dev/null; then
            echo "nginx is running"
        else
            echo "nginx is stopped"
        fi
        ;;
    *)
        echo "Usage: $0 {start|stop|reload|restart|status}"
        exit 1
esac

15. 探测 10.0.0.1-254 存活主机,输出到 ip.txt

#!/bin/bash
> ip.txt
for i in {1..254}; do
    (ping -c1 -W1 10.0.0.$i &>/dev/null && echo 10.0.0.$i) &
done >> ip.txt
wait

16. 统计句子中字母数不大于6的单词

句子:I am oldboy teacher welcome to oldboy training class.

echo "I am oldboy teacher welcome to oldboy training class." | xargs -n1 | awk 'length<=6'

17. 计算从1加到100之和

# 方法1:for循环
sum=0; for i in {1..100}; do sum=$((sum+i)); done; echo $sum
# 方法2:公式
echo $((100 * 101 / 2))

18. while循环实现1加到100

#!/bin/bash
sum=0; i=1
while [ $i -le 100 ]; do
    sum=$((sum+i))
    ((i++))
done
echo $sum

19. 批量创建文件并改名(/oldboy目录,随机字母+oldboy,改为oldgirl)

#!/bin/bash
mkdir -p /oldboy
for i in {1..10}; do
    rand=$(cat /dev/urandom | tr -dc 'a-z' | head -c 10)
    touch "/oldboy/${rand}_oldboy.html"
done
cd /oldboy
for file in *_oldboy.html; do
    mv "$file" "${file//oldboy/oldgirl}"
done

20. 输入两个整数计算加减乘除求余

#!/bin/bash
if [ $# -ne 2 ]; then echo "Usage: $0 num1 num2"; exit 1; fi
echo "加法: $(( $1 + $2 ))"
echo "减法: $(( $1 - $2 ))"
echo "乘法: $(( $1 * $2 ))"
echo "除法: $(( $1 / $2 ))"
echo "求余: $(( $1 % $2 ))"

21. sed删除前5行含字母的行,删除6-10行所有字母

sed '1,5{/[a-zA-Z]/d};6,10{s/[a-zA-Z]//g}' input.txt > output.txt

22. 统计自定义用户(普通用户)个数

awk -F: '$3>=1000 && $3<65534 {c++} END {print c+0}' /etc/passwd

23. 统计最常用的10个命令(基于历史文件)

cat ~/.bash_history | awk '{print $1}' | sort | uniq -c | sort -rn | head -10

24. 计算100以内能被3整除的数之和

sum=0; for i in {1..100}; do [ $((i%3)) -eq 0 ] && sum=$((sum+i)); done; echo $sum

25. 将条件表达式 [ -f /etc/hosts ] && echo ! 改写为if语句

if [ -f /etc/hosts ]; then echo "!"; fi

26. 按单词频率降序排序 / 按字母频率降序排序

文本:the squid project provides a number of resources to assist users design,implement and support squid installations. Please browse the documentation and support sections for more infomation

# 单词频率
echo "..." | tr ' ' '\n' | sort | uniq -c | sort -rn
# 字母频率
echo "..." | grep -o '[a-zA-Z]' | sort | uniq -c | sort -rn

27. 杀死所有名为test.sh的进程

pkill -f test.sh
# 或
ps aux | grep test.sh | grep -v grep | awk '{print $2}' | xargs kill -9

28. 打印文本文档中只有一个数字的行

grep -x '[0-9]\+' input.txt

29. 通过curl状态码判断网站是否正常

status=$(curl -o /dev/null -s -w "%{http_code}" http://example.com)
[ "$status" -eq 200 ] && echo "正常" || echo "异常,状态码 $status"

30. 抓阄程序(输入英文名,生成01-99不重复随机数,数字越大越优先)

#!/bin/bash
> record.txt
while true; do
    read -p "请输入英文名字(输入 quit 退出): " name
    [ "$name" = "quit" ] && break
    while true; do
        num=$((RANDOM % 99 + 1))
        num=$(printf "%02d" $num)
        if ! grep -q "^$num$" record.txt; then
            echo "$num" >> record.txt
            echo "$name $num" | tee -a result.txt
            break
        fi
    done
done
sort -rnk2 result.txt

第三部分:数组与高级控制

31. 普通数组和关联数组定义

普通数组

arr=(a b c)                # 方式1
arr[0]=a; arr[1]=b         # 方式2

关联数组

declare -A assoc
assoc[name]="tom"; assoc[age]=18          # 方式1
declare -A assoc=([name]="tom" [age]=18)  # 方式2

32. 查看数组索引和元素个数

echo ${!array[@]}    # 索引
echo ${#array[@]}    # 元素个数

33. 将数组 array=(1 2 3 4 5 6) 中的3替换为C

array=(1 2 3 4 5 6); array[2]=C; echo ${array[@]}

34. 打印数组每个元素(每行一个)

array=(1 2 3 4 5 6)
for i in "${array[@]}"; do echo $i; done

35. 使用数组判断句子中字母数大于6的单词

句子:I am oldboy teacher welcome to training class

str="I am oldboy teacher welcome to training class"
words=($str)
for w in "${words[@]}"; do
    [ ${#w} -gt 6 ] && echo $w
done

36. 统计Nginx日志中每个IP的访问量

awk '{print $1}' access.log | sort | uniq -c | sort -rn

37. 统计Nginx日志中访问最多的10个IP的流量总和

awk '{ip=$1; bytes=$10} {sum[ip]+=bytes} END {for(ip in sum) print sum[ip], ip}' access.log | sort -rn | head -10 | awk '{total+=$1} END {print "总流量:", total}'

38. 计算所有进程占用内存大小的和(KB)

ps -eo rss --no-headers | awk '{sum+=$1} END {print sum " KB"}'

39. 按照时间生成文件(如2018-05-22.log)写入磁盘状态

logfile=$(date +%Y-%m-%d).log; df -h > "$logfile"

40. 在/backup下创建10个.txt文件,批量修改后缀为.bak,打包,还原

#!/bin/bash
mkdir -p /backup
for i in {1..10}; do touch "/backup/file${i}.txt"; done
cd /backup
for f in *.txt; do mv "$f" "${f}.bak"; done
tar -czf 123.tar.gz *.bak
for f in *.bak; do mv "$f" "${f%.bak}"; done

第四部分:流程控制(break/continue/exit/return)

命令含义
exit退出当前 shell 或脚本,可返回状态码
break跳出当前循环(for、while、until)
continue跳过本次循环剩余部分,进入下一次循环迭代
return用于函数,返回函数执行状态,退出函数体

第五部分:补充综合练习

41. 生成随机数相加直到大于1000,输出总和及最后一个数能否被3整除

#!/bin/bash
sum=0; last=0
while [ $sum -le 1000 ]; do
    last=$((RANDOM % 101))
    sum=$((sum + last))
done
echo "总和: $sum"
if [ $((last % 3)) -eq 0 ]; then
    echo "最后一个数字 $last 能被3整除"
else
    echo "最后一个数字 $last 不能被3整除"
fi

42. 判断/tmp目录下是否有大于4k的文件,输出大小与创建时间

find /tmp -type f -size +4k -printf "%s bytes %t\n" 2>/dev/null

43. 检测重要文件是否被修改(使用指纹)

# 生成指纹:md5sum /important/file > file.md5
# 检查脚本
md5sum -c file.md5 &>/dev/null || echo "文件已被修改" | mail -s "Alert" admin@example.com

44. 防止DOS攻击(监控nginx日志,PV过大则封禁IP)

#!/bin/bash
tail -10000 /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | while read count ip; do
    if [ $count -gt 200 ]; then
        iptables -I INPUT -s $ip -j DROP
        echo "$(date) 封禁 $ip (PV=$count)" >> /var/log/ddos.log
    fi
done

45. 实时监控CPU/磁盘/内存使用率 >80% 报警

#!/bin/bash
while true; do
    cpu=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
    disk=$(df -h / | awk 'NR==2{print $5}' | tr -d '%')
    mem=$(free | awk '/Mem:/ {printf "%.0f", $3/$2 * 100}')
    [ ${cpu%.*} -gt 80 ] && echo "CPU报警: ${cpu}%"
    [ $disk -gt 80 ] && echo "磁盘报警: ${disk}%"
    [ $mem -gt 80 ] && echo "内存报警: ${mem}%"
    sleep 10
done

46. 使用二进制安装nginx(可重复执行)

#!/bin/bash
[ -f /usr/local/nginx/sbin/nginx ] && echo "nginx already installed" && exit 0
yum install -y gcc make pcre-devel zlib-devel
cd /usr/local/src
wget http://nginx.org/download/nginx-1.24.0.tar.gz
tar xf nginx-1.24.0.tar.gz
cd nginx-1.24.0
./configure --prefix=/usr/local/nginx
make && make install
ln -s /usr/local/nginx/sbin/nginx /usr/sbin/nginx
echo "nginx installed successfully"

47. 数据库分库分表备份到/tmp下

#!/bin/bash
backup_dir=/tmp/backup
mkdir -p $backup_dir
for db in $(mysql -e "SHOW DATABASES;" | grep -v Database); do
    mkdir -p $backup_dir/$db
    for table in $(mysql -e "USE $db; SHOW TABLES;" | tail -n +2); do
        mysqldump $db $table > $backup_dir/$db/${table}.sql
    done
done

48. 显示默认环境变量

env   # 或 printenv

49. 函数内设置局部变量

local var=value

50. 显示结果为 $test 的echo命令

echo '\$test'

51. Shell脚本第一行

#!/bin/bash

52. 查看脚本执行过程的参数

bash -x script.sh

sonar-scanner
-Dsonar.projectKey=game
-Dsonar.sources=.
-Dsonar.host.url=http://10.0.0.202:9000
-Dsonar.login=c72a261b3a86d16ba91ccfa0df6879d79ba188e5