14

This entry appears to be about 80% of what I want to do but nothing I've come up with is working: bash: how to delete elements from an array based on a pattern

I have two arrays:

@tapes_in_drives=(SP1026,SP0995,SP0434)

@tapes=(SP0001,SP0002,SP0434,SP0995,SP1026,SP2000,SP3000)

I need a bash solution that can delete all entries present in @tapes_in_drives from @tapes.

I am building a script to automate the ejection of tapes from a tape library based on expiration date, etc.

I tried this:

for i in "${tapes_in_drives[@]}"; do
        tapes=(${tapes[@]//*$i*})
done

but it doesn't work, nothing is removed.

Thanks for any help you can provide.

EDIT:

Here is the code I'm using, I wrote this in Perl originally, hence the @variable definitions and I was super tired when I wrote this question.

CODE:

#!/usr/bin/bash

# If media expires less than this many days, keep it
export days_expired="30"

# Set to 1 to get debug output to console
export debug="1"

# Get list of SPxxxxx tapes that are physically in the library
if [ $debug -eq 1 ]; then echo "Getting tape list"; fi
export tapes=`vmquery -rn 1 -b | tail +4 | awk '{print \$1}' && vmquery -rn 4 -b | tail +4 | awk '{print \$1}'`

if [ $debug -eq 1 ]; then echo ${tapes[@]}; fi

# Query tape drives for tapes
export tapes_in_drives=`ssh srv-reg-nbms-01 "echo 's d q'|/usr/openv/volmgr/bin/tldtest -r /dev/smc0|grep 'Barcode'" | awk '{print $3}' && ssh srv-reg-nbms-02 "echo 's d q'|/usr/openv/volmgr/bin/tldtest -r /dev/smc0|grep 'Barcode'" | awk '{print $3}'`

if [ $debug -eq 1 ]; then
    echo ""
    echo "Tapes in Drives:"
    echo ${tapes_in_drives[@]}
    echo "";
fi


# Remove tapes in drives from list of tapes
for i in "${tapes_in_drives[@]}"; do
    tapes=(${tapes[@]//*$i*})
done

echo "Tape List 2"
echo ${tapes[@]}

Results:

Getting tape list

SP0011 SP0039 SP0402 SP0434 SP0464 SP0516 SP0551 SP0600 SP0604 SP0726 SP0731 SP0765 SP0767 SP0779 SP0781 SP0787 SP0793 SP0794 SP0805 SP0828 SP0830 SP0832 SP0927 SP0928 SP0936 SP0983 SP0995 SP1001 SP1004 SP1008 SP1015 SP1017 SP1026 SP1033 SP1036 SP1038 SP0042 SP0049 SP0150 SP0462 SP0473 SP0517 SP0557 SP0560 SP0642 SP0659 SP0697 SP0712 SP0723 SP0766 SP0777 SP0786 SP0788 SP0792 SP0907 SP0910 SP0923 SP0925 SP0926 SP0940 SP0963 SP0981 SP0986 SP0989 SP0994 SP0999 SP1007 SP1020 SP1021 SP1027 SP1039

Tapes in Drives:

SP1001 SP1038 SP0923 SP0926 SP0925

Tape List 2

SP0011 SP0039 SP0402 SP0434 SP0464 SP0516 SP0551 SP0600 SP0604 SP0726 SP0731 SP0765 SP0767 SP0779 SP0781 SP0787 SP0793 SP0794 SP0805 SP0828 SP0830 SP0832 SP0927 SP0928 SP0936 SP0983 SP0995 SP1001 SP1004 SP1008 SP1015 SP1017 SP1026 SP1033 SP1036 SP1038 SP0042 SP0049 SP0150 SP0462 SP0473 SP0517 SP0557 SP0560 SP0642 SP0659 SP0697 SP0712 SP0723 SP0766 SP0777 SP0786 SP0788 SP0792 SP0907 SP0910 SP0923 SP0925 SP0926 SP0940 SP0963 SP0981 SP0986 SP0989 SP0994 SP0999 SP1007 SP1020 SP1021 SP1027 SP1039

As you can see, the tape names from tapes_in_drives are not being removed from the tapes array.

Community
  • 1
  • 1
user708516
  • 373
  • 2
  • 4
  • 10
  • What's wrong with your solution? I tried it, fixing the syntax errors of course, and it seems to work. – C2H5OH Apr 04 '12 at 22:47
  • why are you using '@' at beginning of variable names? I don't think that is a legitimate char for a variable name. Good luck. – shellter Apr 04 '12 at 22:51

3 Answers3

19

As a comment said, it's just syntax errors.

Space-delimit the array entries, don't use the @, and the function is fine for your example data. Note that it removes any entry containing an entry in tapes_in_drives, not just those that match an entry it exactly.

tapes=(SP0001 SP0002 SP0434 SP0995 SP1026 SP2000 SP3000)
tapes_in_drives=(SP1026 SP0995 SP0434)
for i in "${tapes_in_drives[@]}"; do
         tapes=(${tapes[@]//*$i*})
done

Results:

$ echo ${tapes[0]}
SP0001
$ echo ${tapes[1]}
SP0002
$ echo ${tapes[2]}
SP2000
$ echo ${tapes[3]}
SP3000
$ echo ${tapes[4]}

$

EDIT to respond to the edit in the question

This line:

export tapes=`vmquery -rn 1 -b | tail +4 | awk '{print \$1}' && vmquery -rn 4 -b | tail +4 | awk '{print \$1}'`

And this line:

export tapes_in_drives=`ssh srv-reg-nbms-01 "echo 's d q'|/usr/openv/volmgr/bin/tldtest -r /dev/smc0|grep 'Barcode'" | awk '{print $3}' && ssh srv-reg-nbms-02 "echo 's d q'|/usr/openv/volmgr/bin/tldtest -r /dev/smc0|grep 'Barcode'" | awk '{print $3}'`

initialise tapes and tapes)in_drives as strings, not arrays. You need to add parentheses around the value being assigned to make them into arrays, or the loop won't work. You can also drop the export, it isn't necessary unless you want processes spawned by the script to inherit those shell variables as environment variables.

tapes=(`vmquery -rn 1 -b | tail +4 | awk '{print \$1}' && vmquery -rn 4 -b | tail +4 | awk '{print \$1}'`)

tapes_in_drives=(`ssh srv-reg-nbms-01 "echo 's d q'|/usr/openv/volmgr/bin/tldtest -r /dev/smc0|grep 'Barcode'" | awk '{print $3}' && ssh srv-reg-nbms-02 "echo 's d q'|/usr/openv/volmgr/bin/tldtest -r /dev/smc0|grep 'Barcode'" | awk '{print $3}'`)
je4d
  • 7,628
  • 32
  • 46
  • 1
    This solution doesn't work for the following values: ` tapes=(SP1 SP2 SP3 SP10 SP11 SP12 SP13 SP43) tapes_in_drives=(SP1 SP2 SP3) **Result:** SP43 ` – Dhawal Mar 01 '19 at 23:01
6

Another solution:

tapes_in_drives=( SP1026 SP0995 SP0434 )
tapes=(SP0001 SP0002 SP0434 SP0995 SP1026 SP2000 SP3000)

tps=" ${tapes[*]} "                     # stringify the array

for item in ${tapes_in_drives[@]}; do
  tps=${tps/ ${item} / }                # replace item
done
tapes=( $tps )                          # replace the array
Fritz G. Mehner
  • 16,550
  • 2
  • 34
  • 41
  • Won't work if an input string is shorter than a source one and contains spaces. For example, `("1" "12" "123" "12 3") -> / 12 / / -> ("1" "123" "3")` – Artfaith Apr 17 '21 at 06:28
-1

Stolen from this answer and adapted to your variables:

tapes_in_drives=(SP1026 SP0995 SP0434)
tapes=(SP0001 SP0002 SP0434 SP0995 SP1026 SP2000 SP3000)

free_tapes=( $(printf "%s\n" "${tapes[@]}" "${tapes_in_drives[@]}" | sort | uniq -u) )

echo "${free_tapes[@]}"

Output:

SP0001 SP0002 SP2000 SP3000

The -u switch to uniq makes it "only print unique lines", so it excludes the tapes which are in both arrays.

mivk
  • 13,452
  • 5
  • 76
  • 69
  • This doesn't work when your filter list contains item not in the master list - what you have written gives a difference list, rather than deleting one from the other. – Joe Shanahan Jan 15 '20 at 15:58