问题描述:某个员工的linux系统有大量重复的软件(版本不同),导致使用yum安装软件时报错。现在需要将重复的软件找出来,将低版本的软件删掉。执行rpm -qa | sort 显示如下:

[root@cws85 ~]# rpm -qa | sort 
a52dec-0.7.4-27.el7.x86_64
aalib-libs-1.4.0-0.22.rc5.el7.x86_64
abattis-cantarell-fonts-0.0.25-1.el7.noarch
abrt-2.1.11-52.el7.centos.x86_64
abrt-addon-ccpp-2.1.11-52.el7.centos.x86_64
abrt-addon-kerneloops-2.1.11-52.el7.centos.x86_64
abrt-addon-pstoreoops-2.1.11-52.el7.centos.x86_64
abrt-addon-python-2.1.11-52.el7.centos.x86_64
abrt-addon-vmcore-2.1.11-52.el7.centos.x86_64
abrt-addon-xorg-2.1.11-52.el7.centos.x86_64
abrt-cli-2.1.11-52.el7.centos.x86_64
abrt-console-notification-2.1.11-52.el7.centos.x86_64
abrt-dbus-2.1.11-52.el7.centos.x86_64
abrt-desktop-2.1.11-52.el7.centos.x86_64
abrt-gui-2.1.11-52.el7.centos.x86_64
abrt-gui-libs-2.1.11-52.el7.centos.x86_64
abrt-java-connector-1.0.6-12.el7.x86_64
abrt-libs-2.1.11-52.el7.centos.x86_64
abrt-python-2.1.11-52.el7.centos.x86_64
abrt-retrace-client-2.1.11-52.el7.centos.x86_64
abrt-tui-2.1.11-52.el7.centos.x86_64
accountsservice-0.6.50-4.el7.1.x86_64
accountsservice-libs-0.6.50-4.el7.1.x86_64
acl-2.2.51-14.el7.x86_64
adcli-0.8.1-6.el7_6.1.x86_64
adwaita-cursor-theme-3.28.0-1.el7.noarch
adwaita-gtk2-theme-3.28-2.el7.x86_64
adwaita-icon-theme-3.28.0-1.el7.noarch
adwaita-qt5-1.0-1.el7.x86_64
aic94xx-firmware-30-6.el7.noarch
alsa-firmware-1.0.28-2.el7.noarch
alsa-lib-1.1.6-2.el7.x86_64
alsa-plugins-pulseaudio-1.1.6-1.el7.x86_64
alsa-tools-firmware-1.1.0-1.el7.x86_64
alsa-utils-1.1.6-1.el7.x86_64
anaconda-core-21.48.22.121-1.el7.centos.x86_64
anaconda-core-21.48.22.147-1.el7.centos.0.1.x86_64
anaconda-gui-21.48.22.147-1.el7.centos.0.1.x86_64
anaconda-tui-21.48.22.121-1.el7.centos.x86_64                 #这是重复软件的低版本
anaconda-tui-21.48.22.147-1.el7.centos.x86_64          #这是重复软件的高版本
anaconda-widgets-21.48.22.147-1.el7.centos.0.1.x86_64
..........省略
总共2586个软件

要求:如上所示,需要将低版本的软件删除,如下是例外:

[root@cws85 ~]# rpm -qa | grep audit-libs-[0-9]
audit-libs-2.8.4-4.el7.i686
audit-libs-2.8.4-4.el7.x86_64
虽然是重复软件,但一个是x86_64,一个是i686,这样不能删除,只能删除重复的_x86_64结尾的软件

数组:脚本中用到了数组,下面是数组的部分内容概述:

1.数组定义:
declare -a 数组名 #定义索引数组,下标从0开始
declare -A 数组名 #定义关联数组,下标为任意字符

2.数组赋值与复制:
2.1.可以通过命令替换的方式给数组赋值: declare -a SOFT1=($(rpm -qa | sort)) #数组SOFT1保存着所有软件
2.2.数组复制操作:linux2=(${linux1[@]}) #数组linux2复制了数组linux1的内容

3.数组的字符串操作:
3.1.字符串的操作符可以用在数组上,如:${#string} 用于数组 ${#array[@]},等等等等。
3.2.数组遍历: for i in ${!SOFT[@]} #注意加上感叹号可以遍历数组中的值,假设SOFT的下标最大是2586,通过for循环可以遍历完数组,i的值从0依次到2586。

脚本内容:

#!/bin/bash
declare -a SOFT1=($(rpm -qa | sort))      #数组SOFT1保存所有软件名称

for i in ${!SOFT1[*]}                 #遍历数组SOFT1
 do
   declare -a SOFT2[$i]=${SOFT1[$i]%%-[0-9]*}        #数组SOFT2复制数组SOFT1的部分内容,%%是字符串截取符号,相当于去掉软件的版本号,只保留软件名称
 done

for i in $(seq 0 $((${#SOFT2[@]}-1)))        #遍历数组SOFT2
  do
     let "j=$i+5"  
     for (( ;i>/tmp/soft  && continue    #小于2输出该软件没有重复的版本
              elif (( "${#SOFT3[@]}" > 2 ))     #如果下标大于2,也就是(rpm -qa | grep 重复软件)有多于2个软件
                then
                   X86=0 I686=0           #初始化两个变量,用于后面比较
                   for i in ${!SOFT3[*]}      #遍历数组SOFT3
                     do
                       [[ "${SOFT3[$i]##*.}" == "i686" ]] && I686=$((I686+1)) || X86=$((X86+1))     #如果软件名结尾是i686,那么变量I686加1,否则X86加1
                     done
                       (( "$X86" >= "$I686" )) && echo "${SOFT3[0]} can1 delete" >>/tmp/soft      #如果X86值大于I686,输出软件能够删除
               elif (( "${#SOFT3[@]}" == 2 ))         #如果下标等于2,也就是(rpm -qa | grep 重复软件)有两个
                then
                   [[ "${SOFT3[0]##*.}" == "i686" || "${SOFT3[1]##*.}" == "i686" ]] && { echo "${SOFT3[0]} only two packages but has I686" >>/tmp/soft ; continue ; }          #两个软件里只要有1个是i686结尾的,输出不能删除
                   echo "${SOFT3[0]} can2 delete" >>/tmp/soft
             fi
         fi

     done
  done

脚本说明

1.数组SOFT1保存所有软件的完整名称,数组SOFT2是通过数组复制加上字符串的替换,截取出不带版本名称的软件名,比如 SOFT1[4]=abrt-addon-ccpp-2.1.11-52.el7.centos.x86_64, SOFT2[4]=abrt-addon-ccpp。

2.利用for循环遍历数组SOFT2来检测那些是重复软件,如下:

for i in $(seq 0 $((${#SOFT2[@]}-1)))       #遍历数组SOFT2
do    
     let  "j=$i+5"       #j比i大5,用于测试某个软件名与它后面的5个软件名是否相等,因为是通过rpm -qa | sort排序过,所以软件名都是按照字母顺序排的,比较5个就可以了。  
     for (( ;i

3.如果有重复,需要处理软件后缀名的问题,只有重复的以X86结尾的软件名才输出该软件可以删除。利用数组SOFT3=($(rpm -qa | grep "^${SOFT2[$i]}-[0-9]" | sort)) 来保存搜索出的重复软件,然后在进行判断比较

脚本输出结果:输出被重定向到文件里,文件内容如下:

anaconda-core-21.48.22.121-1.el7.centos.x86_64 can2 delete
anaconda-tui-21.48.22.121-1.el7.centos.x86_64 can2 delete
audit-libs-2.8.4-4.el7.i686 only two packages but has I686
avahi-libs-0.6.31-17.el7.x86_64 can2 delete
bzip2-libs-1.0.6-13.el7.i686 only two packages but has I686
copy-jdk-configs-2.2-5.el7_4.noarch can2 delete
cracklib-2.9.0-11.el7.i686 only two packages but has I686
cryptsetup-libs-1.7.4-3.el7_4.1.x86_64 can2 delete
dbus-1.10.24-13.el7_6.x86_64 can2 delete
dbus-libs-1.10.24-13.el7_6.x86_64 can2 delete
device-mapper-event-libs-1.02.149-10.el7_6.3.x86_64 can2 delete
elfutils-libelf-0.172-2.el7.i686 only two packages but has I686
elfutils-libs-0.172-2.el7.i686 only two packages but has I686
fprintd-0.5.0-4.0.el7_0.x86_64 can2 delete
freetype-2.8-12.el7_6.1.i686 only two packages but has I686
glib2-2.54.2-2.el7.x86_64 can2 delete
glibc-2.17-260.el7_6.6.i686 only two packages but has I686
........省略
总共81行,能删除的有52行(有少数错误,后面说明)

脚本调试信息:sh -x 脚本名显示的部分内容如下:

+ SOFT1=($(rpm -qa | sort))      #数组SOFT1保存完整软件名
++ rpm -qa
++ sort
+ declare -a SOFT1
+ for i in '${!SOFT1[*]}'                #遍历数组SOFT1 
+ declare -a 'SOFT2[0]=a52dec'      #给数组SOFT2赋值,只保留软件名
+ for i in '${!SOFT1[*]}'
+ declare -a 'SOFT2[1]=aalib-libs'
+ for i in '${!SOFT1[*]}'
+ declare -a 'SOFT2[2]=abattis-cantarell-fonts'
+ for i in '${!SOFT1[*]}'
+ declare -a 'SOFT2[3]=abrt'
+ for i in '${!SOFT1[*]}'
+ declare -a 'SOFT2[4]=abrt-addon-ccpp'
+ ..............省略,总共2586个

++ seq 0 2585   
+ for i in '$(seq 0 $((${#SOFT2[@]}-1)))'          #遍历数组SOFT2,测试哪些是重复软件
+ let j=0+5       #总共比较5次
+ (( 1 ))
+ (( i 2  ))
+ ((  2 == 2  ))      #刚好有两个包
+ [[ x86_64 == i686 ]]     #第一个包后缀是x86_64
+ [[ x86_64 == i686 ]]    #第二个包后缀是x86_64
+ echo 'anaconda-core-21.48.22.121-1.el7.centos.x86_64 can2 delete'      #输出能删除
+ ..........................省略

脚本输出错误:脚本输出文件里总共有52行是能删除的软件,其中有几个是错误的,由于软件名称导致的错误,如下:

错误1: 软件名称: 字符-数字-字符-版本号 导致判断错误
java-1.8.0-openjdk-1.8.0.222.b10-0.el7_6.x86_64 can1 delete
java-1.8.0-openjdk-1.8.0.222.b10-0.el7_6.x86_64 can1 delete
SOFT2[$i]=${SOFT1[$i]%%-[0-9]*} #问题就在截取软件名称上,SOFT2=java, 正确的应该是SOFT2=java-1.8.0-openjdk。

错误2:错误的软件名称结尾,该软件没有以.X86或其他结尾
gpg-pubkey-0c1289c0-58c6ad7d can1 delete
gpg-pubkey-0c1289c0-58c6ad7d can1 delete
gpg-pubkey-0c1289c0-58c6ad7d can1 delete
在给SOFT1赋值的时候需要过滤掉这种没有以.X86或其他后缀结尾的软件

总结:很少在脚本中用到数组,通过这个脚本加深了对数组的掌握与应用。不知道怎么解决脚本输出中的错误1这种问题,大部分的软件都能正确去掉版本号,个别的软件名就不行,感觉总会有漏洞。该脚本主要在于练习数组的应用,同时也能从二千多个软件里面找出重复软件,虽然有个别错误,但也会比用眼睛找重复软件好,对照着输出文件来删除软件会轻松很多。