shell script exercises -- arrays

Keywords: Linux CentOS Anaconda RPM

Problem Description: An employee's linux system has a large number of duplicate software (different versions), resulting in errors when installing software using yum. Now we need to find out the duplicate software and delete the low version software. Execute rpm-qa | sort as follows:

[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                 #This is the low version of duplicate software.
anaconda-tui-21.48.22.147-1.el7.centos.x86_64          #This is a high version of duplicate software.
anaconda-widgets-21.48.22.147-1.el7.centos.0.1.x86_64
..........ellipsis
//A total of 2586 software

Requirements: As indicated above, you need to delete the lower version of the software, with the following exceptions:

[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
Although it is duplicate software, one is x86_64 and the other is i686, so it can not be deleted, only the duplicate software at the end of _x86_64 can be deleted.

Arrays: Arrays are used in scripts. The following is a partial overview of arrays:

1. Array definition:
Declare-a array name# defines index arrays, subscripts start at 0
Declare-A array name # defines associative arrays, subscribed to arbitrary characters

2. Array assignment and replication:
2.1. Arrays can be assigned by command substitution: declare-a SOFT1= ($(rpm-qa | sort)# array SOFT1 holds all software
2.2. Array replication operation: Linux 2=(${linux 1 [@]}) array Linux 2 replicates the contents of array Linux 1

3. String operations of arrays:
3.1. String operators can be used on arrays, such as: ${string} for array ${array [@]}, and so on.
3.2. Array traversal: for i in ${!SOFT [@]} # Attention plus exclamation marks can traverse the values of the array, assuming that the maximum subscript of SOFT is 2586, the array can be traversed through the for loop, and the value of i is in turn from 0 to 2586.

The script content:

#!/bin/bash
declare -a SOFT1=($(rpm -qa | sort))      #Array SOFT1 saves all software names

for i in ${!SOFT1[*]}                 #Traversal array SOFT1
 do
   declare -a SOFT2[$i]=${SOFT1[$i]%%-[0-9]*}        #Array SOFT2 replicates part of the array SOFT1,%% is a string interception symbol, which is equivalent to removing the version number of the software, leaving only the name of the software.
 done

for i in $(seq 0 $((${#SOFT2[@]}-1)))        #Traversal array SOFT2
  do
     let "j=$i+5"  
     for (( ;i<j;j--))            #Jump out of the loop when i is less than j and execute the loop five times
       do
         if [ "${SOFT2[$i]}" == "${SOFT2[$j]}" ]     #Test for duplicate software
           then
              declare -a SOFT3=($(rpm -qa | grep "^${SOFT2[$i]}-[0-9]" | sort))    #If the software is duplicated, perform rpm-qa | grep to duplicate the software name and assign it to the array SOFT3
              if (( "${#SOFT3[@]}" < 2 ))     #Check if the subscript number of SOFT3 array is less than 2 (with an additional layer of insurance)
                then
                   echo "${SOFT2[$i]} no repeat version" >>/tmp/soft  && continue    #No duplicate version of the software is output less than 2
              elif (( "${#SOFT3[@]}" > 2 ))     #If the subscript is greater than 2, that is (rpm-qa | grep duplicate software) there are more than two software
                then
                   X86=0 I686=0           #Initialize two variables for later comparison
                   for i in ${!SOFT3[*]}      #Traversal array SOFT3
                     do
                       [[ "${SOFT3[$i]##*.}" == "i686" ]] && I686=$((I686+1)) || X86=$((X86+1))     #If the end of the software name is i686, then variable I686 plus 1, otherwise X86 plus 1
                     done
                       (( "$X86" >= "$I686" )) && echo "${SOFT3[0]} can1 delete" >>/tmp/soft      #If the X86 value is greater than I686, the output software can be deleted.
               elif (( "${#SOFT3[@]}" == 2 ))         #If the subscript equals 2, there are two (rpm-qa | grep duplicate software)
                then
                   [[ "${SOFT3[0]##*.}" == "i686" || "${SOFT3[1]##*.}" == "i686" ]] && { echo "${SOFT3[0]} only two packages but has I686" >>/tmp/soft ; continue ; }          #As long as one of the two softwares ends with i686, the output cannot be deleted.
                   echo "${SOFT3[0]} can2 delete" >>/tmp/soft
             fi
         fi

     done
  done

The script description:

1. Array SOFT1 preserves the full name of all software. Array SOFT2 intercepts the name of software without version name by array copy and string replacement, such as SOFT1 [4]=abrt-addon-ccpp-2.1.11-52.el7.centos.x86_64, SOFT2 [4]=abrt-addon-ccpp.

2. Use for loop traversal array SOFT2 to detect repetitive software, as follows:

for i in $(seq 0 $((${#SOFT2[@]}-1)))       #Traversal array SOFT2
do    
     let  "j=$i+5"       #j is 5 bigger than i. It is used to test whether a software name is equal to the five software names behind it. Because it is sorted by rpm-qa | sort, so the software names are arranged in alphabetical order. It is OK to compare five.  
     for (( ;i<j;j--))        
         do
             if [ "${SOFT2[$i]}" == "${SOFT2[$j]}" ]         #Compare duplicate software names

3. If there is duplication, we need to deal with the problem of software suffix names. Only duplicate software names ending with X86 can be output to delete the software. Use the array SOFT3=($(rpm-qa | grep"^${SOFT2 [$i]} - [0-9]" | sort)) to save the duplicate software searched, and then compare the results.

Output of the script: The output is redirected to a file, which reads as follows:

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
........ellipsis
//A total of 81 lines, 52 lines can be deleted (a few errors, explained later)

Script debugging information: sh-x script name shows part of the content as follows:

+ SOFT1=($(rpm -qa | sort))      #Array SOFT1 saves the complete software name
++ rpm -qa
++ sort
+ declare -a SOFT1
+ for i in '${!SOFT1[*]}'                #Traversal array SOFT1 
+ declare -a 'SOFT2[0]=a52dec'      #Assign SOFT2 to the array, leaving only the software name
+ 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'
+ ..............Omitted, a total of 2586

++ seq 0 2585   
+ for i in '$(seq 0 $((${#SOFT2[@]}-1)))'          #Traverse the array SOFT2 and test which is duplicate software
+ let j=0+5       #A total of five comparisons
+ (( 1 ))
+ (( i<j ))
+ '[' a52dec == abrt-addon-kerneloops ']'
+ (( j-- ))
+ (( i<j ))
+ '[' a52dec == abrt-addon-ccpp ']'
+ (( j-- ))
+ (( i<j ))
+ '[' a52dec == abrt ']'
+ (( j-- ))
+ (( i<j ))
+ '[' a52dec == abattis-cantarell-fonts ']'
+ (( j-- ))
+ (( i<j ))
+ '[' a52dec == aalib-libs ']'                             #a53dec... is not a duplicate software
+ (( j-- ))
+ (( i<j ))
+ for i in '$(seq 0 $((${#SOFT2[@]}-1)))'     #Compare the next, array subscript plus 1
+ let j=1+5
...........................................................................ellipsis
+ for i in '$(seq 0 $((${#SOFT2[@]}-1)))'
+ let j=35+5             #Array subscripts to 35
+ (( 1 ))
+ (( i<j ))
+ '[' anaconda-core == anaconda-widgets ']'
+ (( j-- ))
+ (( i<j ))
+ '[' anaconda-core == anaconda-tui ']'
+ (( j-- ))
+ (( i<j ))
+ '[' anaconda-core == anaconda-tui ']'
+ (( j-- ))
+ (( i<j ))
+ '[' anaconda-core == anaconda-gui ']'
+ (( j-- ))
+ (( i<j ))
+ '[' anaconda-core == anaconda-core ']'                                 #Find duplicate software here
+ SOFT3=($(rpm -qa | grep "^${SOFT2[$i]}-[0-9]" | sort))     #Array SOFT3 assignment = (rpm-qa | grep ^ anaconda-core - [0-9] | sort)
++ rpm -qa
++ sort
++ grep '^anaconda-core-[0-9]'
+ declare -a SOFT3
+ ((  2 < 2  ))
+ ((  2 > 2  ))
+ ((  2 == 2  ))      #There are just two bags.
+ [[ x86_64 == i686 ]]     #The first package suffix is x86_64
+ [[ x86_64 == i686 ]]    #The second package suffix is x86_64
+ echo 'anaconda-core-21.48.22.121-1.el7.centos.x86_64 can2 delete'      #Output can be deleted
+ ..........................ellipsis

Script Output Errors: A total of 52 lines of script output files are deletable software, several of which are errors due to the name of the software, as follows:

Error 1: Software Name: Character-Number-Character-Version Number Causes Judgment Error
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]*} # is the problem with intercepting software names, SOFT2=java, and the correct answer should be SOFT2=java-1.8.0-openjdk.

Error 2: The wrong software name ends with. X86 or something.
gpg-pubkey-0c1289c0-58c6ad7d can1 delete
gpg-pubkey-0c1289c0-58c6ad7d can1 delete
gpg-pubkey-0c1289c0-58c6ad7d can1 delete
When assigning values to SOFT1, you need to filter out software that does not end with. X86 or other suffixes.

Summary: Arrays are seldom used in scripts. This script deepens the understanding and application of arrays. Do not know how to solve the problem of error 1 in script output, most software can correctly remove version number, individual software name is not good, feel there will always be loopholes. The script is mainly used to practice the application of arrays, but also can find duplicate software from more than 2,000 software. Although there are individual errors, it is better than using eyes to find duplicate software. It is much easier to delete software by comparing output files.

Posted by hammad on Tue, 10 Sep 2019 03:26:33 -0700