Bash:从一个数组中删除另一个数组中存在的项目

14

这篇文章大致涵盖了我想要做的80%,但我试过的方法都不起作用:bash:如何根据模式从数组中删除元素

我有两个数组:

@tapes_in_drives=(SP1026,SP0995,SP0434)

@tapes=(SP0001,SP0002,SP0434,SP0995,SP1026,SP2000,SP3000)
我需要一个bash解决方案,可以从@tapes中删除所有存在于@tapes_in_drives中的条目。
我正在构建一个脚本来自动弹出磁带库中的磁带,根据到期日期等条件。
我尝试了这个:
for i in "${tapes_in_drives[@]}"; do
        tapes=(${tapes[@]//*$i*})
done

但它无法正常工作,没有任何内容被删除。

感谢您提供的任何帮助。

编辑:

这是我正在使用的代码,最初我用Perl编写了它,因此有@变量定义,而且我在写这个问题时非常累。

代码:

#!/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[@]}

结果:

获取磁带列表

在驱动器中的磁带:

SP1001 SP1038 SP0923 SP0926 SP0925

磁带列表 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

可以看到,从tapes_in_drives中获取的磁带名称没有被从tapes数组中删除。


你的解决方案有什么问题吗?我试过了,当然修复了语法错误,它似乎可以工作。 - C2H5OH
你为什么在变量名的开头使用 '@' 符号呢?我认为这不是一个合法的变量名字符。祝好运。 - shellter
3个回答

19

正如评论所说,这只是语法错误。

用空格分隔数组条目,不要使用@,对于您的示例数据,该函数很好用。请注意,它会删除包含在tapes_in_drives中的任何条目,而不仅仅是完全匹配的条目。

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

结果:

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

$

编辑以回应问题中的编辑

这一行:

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

还有这行代码:

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}'`

tapestapes)in_drives初始化为字符串,而不是数组。您需要在分配的值周围添加括号以将它们转换为数组,否则循环将无法工作。您还可以删除export,除非您希望由脚本生成的进程将这些shell变量继承为环境变量。

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}'`)

1
此解决方案不适用于以下数值: tapes=(SP1 SP2 SP3 SP10 SP11 SP12 SP13 SP43) tapes_in_drives=(SP1 SP2 SP3) **结果:** SP43 - Dhawal

6
另一个解决方案:
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

如果输入字符串比源字符串短并且包含空格,则无法正常工作。例如,("1" "12" "123" "12 3") -> / 12 / / -> ("1" "123" "3") - Artfaith

-1

这个答案中盗取并根据您的变量进行调整:

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[@]}"

输出:

SP0001 SP0002 SP2000 SP3000

-u开关用于uniq命令,使其“仅打印唯一行”,因此它排除了同时存在于两个数组中的磁带。


当您的筛选列表包含主列表中没有的项目时,这种方法无法正常工作 - 您所编写的代码会生成一个差异列表,而不是从另一个列表中删除一个项目。 - Joe Shanahan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接