如果值不在 Shell 脚本中的 Array2 中而不循环,则删除或替换 Array1 中的值?
Remove or Replace Value in Array1 If Value Isn't in Array2 in Shell Script Without Looping?
编辑:
我已将 allVals
的定义更改为有点 cleaner/simpler:
allVals=( $( printf '%s\n' \
$( printf '%s\n' \
{0..9}{0..9}{0,3,5,7} | \
sed -e 's#^0*##g' ) | \
awk '>='"$valMin"' && <='"$valMax" ) \
${exptVals[@]} )
我有一个简短的 BASH script
用于为辅助可执行文件生成 space 分隔的配置文件。脚本部分正在确定将哪些值打印到第 1 列。
为此,我的脚本使用 brace expansion 创建具有以下规则的整数数组:
- 数字不超过
3
位。
- 数字是
integer
(无小数)。
5
的产品必须包含在系列中。
- 我需要在
[#][#]0
和 [#][#]5
之间至少有一个 spaced 点(即以 3
或 7
结尾的数字,因为合适)。
我使用 sed
来清理第二个最重要数字为空的情况(我可能会用 '0' 替换 ' ' 并通过删除前导 '0' 来写一个更简单的等价物,当我解决它...)。
无论如何,这些是我输入到第二个程序中的值,用于为某些属性生成计算预测。我还想确保包括与我拥有的某些实验值相对应的数字...所以我通过创建一个实验值数组然后将两个数组合并在一起,对它们进行排序并删除冗余值来做到这一点。
下面给出了脚本(这是一个单行本 -- 为了便于阅读,我已将其编辑为脚本形式):
#!/bin/bash
lineItem5=61
valMax=433
valMin=260
exptVals=( 257 261 265 269 273 277 281 285 289 293 297 \
301 305 309 313 317 321 325 329 333 337 341 \
345 349 353 357 361 365 369 373 377 381 385 \
389 393 397 401 405 409 413 417 421 425 429 \
433 )
allVals=( $( printf '%s\n' \
$( printf '%s\n' {' ',{1..9}}{' ',{1..9}}{0,3,5,7} | \
sed -e 's# \([1-9]\) 0 [1-9] 3 [1-9] 5 [1-9] 7 # 0 3 5 7 #g' ) | \
awk '>='"$valMin"' && <='"$valMax" ) \
${exptVals[@]} )
sortVals=( $( printf '%s\n' ${allVals[@]} | sort -nr | uniq ) )
for ((t=0;t<${#sortVals[@]};t++)); do
printf '%s\n' "${sortVals[t]}"' -4000 -4000 200 '"${lineItem5}"' -1.0'
done
unset exptVals allVals sortVals
它有效,但我想减少行数(这相当于评估的点,因此等于计算成本)并改善值的间距(这提高了我的统计准确性,因为输出属性的每个点都取决于根据之前的计算)。
具体来说,如果遇到序列 ##7 ##8
,我想删除值 ##7
,同样,如果遇到序列 ##2 ##3
...,我想删除值 ##3
...如果在我的实验值列表中找不到 ##3
或 ##7
值。我还想将 ##3 ##4
更改为 ##2 ##4
并将 ##6 ##7
更改为 ##6 ##8
以改善间距 - 但前提是 ##3
或 ##7
不在实验序列中。
到目前为止,我能想到的最好的方法是做类似
的事情
valStart=0
for ((e=0; e<${#exptVals[@]}; e++)); do
for ((v=valStart; v<${#allT[@]}; v++)); do
if [[ ${allVals[v]} -ge ${exptVals[$((e+1))]} ]]; then
valStart=v
break
else
#Do edits to list here...
fi
done
done
代码尚未完成,但我认为它的效率适中,因为我不必完全遍历第二个列表...只是其中的一小部分(我的实验列表是有序的) .
但我觉得有更简单的方法 delete 'Y' from 'X Y' if 'Y' is not in array $vals
或 change 'Y' to 'Z' for 'X Y' if 'Y' is not in array $vals
?
有没有一种简单的方法可以在单个表达式中使用某种内置完成:
delete 'Y' from 'X Y' if 'Y' is not in array $vals
change 'Y' to 'Z' for 'X Y' if 'Y' is not in array $vals
...这不涉及在 bash 式循环(我的蛮力方法)中遍历值?
为什么不使用 awk
将数字生成为数字序列,而不是按模式生成数字?
例如,
$ awk -v from=100 -v to=200 -v ORS=' ' 'BEGIN{for(i=from;i<=to-10;i+=10)
print i,i+3,i+5,i+7; ORS="\n"; print""}'
100 103 105 107 110 113 115 117 120 123 125 127 130 133 135 137 140 143 145 147 150 153 155 157 160 163 165 167 170 173 175 177 180
183 185 187 190 193 195 197
您的脚本会调用 sed 和 awk 来删除由您使用的大括号扩展创建的空格。一个更简单的大括号扩展是:
$ echo {0..9}{0..9}{0,3,5,7}
前导 0
的问题很容易用 printf '%3.0f'
解决。
将创建一个较短的列表(作为示例):
$ printf '%3.0f ' {0..1}{0..9}{0,3,5,7}
0 3 5 7 10 13 15 17 20 23 25 27 30 33 35 37 40 43
45 47 50 53 55 57 60 63 65 67 70 73 75 77 80 83 85 87
90 93 95 97 100 103 105 107 110 113 115 117 120 123 125 127 130 133
135 137 140 143 145 147 150 153 155 157 160 163 165 167 170 173 175 177
180 183 185 187 190 193 195 197
解决此问题后,我们需要将值限制在 valMin
和 valMax
之间。
与其调用外部 awk 来处理短列表,不如使用循环。通过排序(仅调用一次)和打印,此脚本的功能与您的脚本大致相同,但外部调用却少得多:
#!/bin/bash
lineItem5=61 valMin=260 valMax=433
exptVals=( 257 261 265 269 273 277 281 285 289 293 297 \
301 305 309 313 317 321 325 329 333 337 341 \
345 349 353 357 361 365 369 373 377 381 385 \
389 393 397 401 405 409 413 417 421 425 429 \
433 )
for v in $( printf '%3.0f\n' {0..9}{0..9}{0,3,5,7} )
do (( v>=valMin && v<=valMax )) && allVals+=( "$v" )
done
sortVals=( $(printf '%s\n' "${allVals[@]}" "${exptVals[@]}"|sort -nu) )
printf '%s ' "${sortVals[@]}"
现在我们进入您问题的核心。如何:
remove the value ##7 if the sequence ##7 ##8 is encountered
通常的做法是调用 sed
。类似于:
printf '%s ' "${sortVals[@]}" | sed -e 's/\(..7 \)\(..8\)//g'
这会将 ..7 ..8
转换为 ..8
(反向引用 \2)。
然后,您可以添加更多过滤器以进行更多更改。类似于:
printf '%s ' "${sortVals[@]}" |
sed -e 's/\(..7 \)\(..8\)//g' |
sed -e 's/\(..\)3\( ..4\)//g'
echo
这将解决 ..7 ..8
到 ..8
和 ..3 ..4
到 ..2 ..4
项。
但是您的要求是:
but only if the ##3 or ##7 value is not found in my list
见面比较复杂。我们需要使用 grep 扫描所有值,并为每个选项执行不同的代码。一种常用的解决方案是使用 grep:
if printf '%s ' "${sortVals[@]}" | grep -Eq '..3|..7'; then
cmd2=(cat)
else
cmd2=(sed -e 's/\(..2\)\( ..3\)//g')
fi
但这意味着针对每个条件使用 grep 扫描所有值。
创建的命令:cmd2
是一个数组,可以这样使用:
printf '%s ' "${sortVals[@]}" |
sed -e 's/\(..7 \)\(..8\)//g' | "${cmd2[@]}" |
sed -e 's/\(..\)3\( ..4\)//g' | "${cmd4[@]}"
echo
没有 grep
您正在测试的值只是最后一位,可以使用模 10 数学运算轻松提取。并制作 easier/faster
值的测试,我们可以像这样创建一个索引数组:
unset indexVals; declare -A indexVals
for v in "${sortVals[@]}"; do indexVals[$((v%10))]=1; done
这只是一次值扫描,没有调用外部工具,并且大大简化了值测试(例如,对于 ..2
或 ..3
):
(( ${indexVals[2]-0} || ${indexVals[3]-0} ))
包含所有更改的脚本是:
#!/bin/bash
lineItem5=61 valMin=260 valMax=433
exptVals=( 257 261 265 269 273 277 281 285 289 293 297 \
301 305 309 313 317 321 325 329 333 337 341 \
345 349 353 357 361 365 369 373 377 381 385 \
389 393 397 401 405 409 413 417 421 425 429 \
433 )
for v in $( printf '%3.0f\n' {0..9}{0..9}{0,3,5,7} )
do (( v>=valMin && v<=valMax )) && allVals+=( "$v" )
done
sortVals=( $(printf '%s\n' "${allVals[@]}" "${exptVals[@]}" | sort -nu) )
unset indexVals; declare -A indexVals
for v in "${sortVals[@]}"; do indexVals[$((v%10))]=1; done
cmd1=( sed -e 's/\(..7 \)\(..8\)//g' )
(( ${indexVals[2]-0} || ${indexVals[3]-0} )) &&
cmd2=( cat ) ||
cmd2=( sed -e 's/\(..2\)\( ..3\)//g' )
cmd3=( sed -e 's/\(..\)3\( ..4\)//g' )
(( ${indexVals[3]-0} || ${indexVals[7]-0} )) &&
cmd4=( cat ) ||
cmd4=( sed -e 's/\(..6 ..\)7//g' )
printf '%s ' "${sortVals[@]}" | "${cmd1[@]}" | "${cmd2[@]}" |
"${cmd3[@]}" | "${cmd4[@]}" ; echo
编辑:
我已将
allVals
的定义更改为有点 cleaner/simpler:
allVals=( $( printf '%s\n' \
$( printf '%s\n' \
{0..9}{0..9}{0,3,5,7} | \
sed -e 's#^0*##g' ) | \
awk '>='"$valMin"' && <='"$valMax" ) \
${exptVals[@]} )
我有一个简短的 BASH script
用于为辅助可执行文件生成 space 分隔的配置文件。脚本部分正在确定将哪些值打印到第 1 列。
为此,我的脚本使用 brace expansion 创建具有以下规则的整数数组:
- 数字不超过
3
位。 - 数字是
integer
(无小数)。 5
的产品必须包含在系列中。- 我需要在
[#][#]0
和[#][#]5
之间至少有一个 spaced 点(即以3
或7
结尾的数字,因为合适)。
我使用 sed
来清理第二个最重要数字为空的情况(我可能会用 '0' 替换 ' ' 并通过删除前导 '0' 来写一个更简单的等价物,当我解决它...)。
无论如何,这些是我输入到第二个程序中的值,用于为某些属性生成计算预测。我还想确保包括与我拥有的某些实验值相对应的数字...所以我通过创建一个实验值数组然后将两个数组合并在一起,对它们进行排序并删除冗余值来做到这一点。
下面给出了脚本(这是一个单行本 -- 为了便于阅读,我已将其编辑为脚本形式):
#!/bin/bash
lineItem5=61
valMax=433
valMin=260
exptVals=( 257 261 265 269 273 277 281 285 289 293 297 \
301 305 309 313 317 321 325 329 333 337 341 \
345 349 353 357 361 365 369 373 377 381 385 \
389 393 397 401 405 409 413 417 421 425 429 \
433 )
allVals=( $( printf '%s\n' \
$( printf '%s\n' {' ',{1..9}}{' ',{1..9}}{0,3,5,7} | \
sed -e 's# \([1-9]\) 0 [1-9] 3 [1-9] 5 [1-9] 7 # 0 3 5 7 #g' ) | \
awk '>='"$valMin"' && <='"$valMax" ) \
${exptVals[@]} )
sortVals=( $( printf '%s\n' ${allVals[@]} | sort -nr | uniq ) )
for ((t=0;t<${#sortVals[@]};t++)); do
printf '%s\n' "${sortVals[t]}"' -4000 -4000 200 '"${lineItem5}"' -1.0'
done
unset exptVals allVals sortVals
它有效,但我想减少行数(这相当于评估的点,因此等于计算成本)并改善值的间距(这提高了我的统计准确性,因为输出属性的每个点都取决于根据之前的计算)。
具体来说,如果遇到序列 ##7 ##8
,我想删除值 ##7
,同样,如果遇到序列 ##2 ##3
...,我想删除值 ##3
...如果在我的实验值列表中找不到 ##3
或 ##7
值。我还想将 ##3 ##4
更改为 ##2 ##4
并将 ##6 ##7
更改为 ##6 ##8
以改善间距 - 但前提是 ##3
或 ##7
不在实验序列中。
到目前为止,我能想到的最好的方法是做类似
的事情valStart=0
for ((e=0; e<${#exptVals[@]}; e++)); do
for ((v=valStart; v<${#allT[@]}; v++)); do
if [[ ${allVals[v]} -ge ${exptVals[$((e+1))]} ]]; then
valStart=v
break
else
#Do edits to list here...
fi
done
done
代码尚未完成,但我认为它的效率适中,因为我不必完全遍历第二个列表...只是其中的一小部分(我的实验列表是有序的) .
但我觉得有更简单的方法 delete 'Y' from 'X Y' if 'Y' is not in array $vals
或 change 'Y' to 'Z' for 'X Y' if 'Y' is not in array $vals
?
有没有一种简单的方法可以在单个表达式中使用某种内置完成:
delete 'Y' from 'X Y' if 'Y' is not in array $vals
change 'Y' to 'Z' for 'X Y' if 'Y' is not in array $vals
...这不涉及在 bash 式循环(我的蛮力方法)中遍历值?
为什么不使用 awk
将数字生成为数字序列,而不是按模式生成数字?
例如,
$ awk -v from=100 -v to=200 -v ORS=' ' 'BEGIN{for(i=from;i<=to-10;i+=10)
print i,i+3,i+5,i+7; ORS="\n"; print""}'
100 103 105 107 110 113 115 117 120 123 125 127 130 133 135 137 140 143 145 147 150 153 155 157 160 163 165 167 170 173 175 177 180 183 185 187 190 193 195 197
您的脚本会调用 sed 和 awk 来删除由您使用的大括号扩展创建的空格。一个更简单的大括号扩展是:
$ echo {0..9}{0..9}{0,3,5,7}
前导 0
的问题很容易用 printf '%3.0f'
解决。
将创建一个较短的列表(作为示例):
$ printf '%3.0f ' {0..1}{0..9}{0,3,5,7}
0 3 5 7 10 13 15 17 20 23 25 27 30 33 35 37 40 43
45 47 50 53 55 57 60 63 65 67 70 73 75 77 80 83 85 87
90 93 95 97 100 103 105 107 110 113 115 117 120 123 125 127 130 133
135 137 140 143 145 147 150 153 155 157 160 163 165 167 170 173 175 177
180 183 185 187 190 193 195 197
解决此问题后,我们需要将值限制在 valMin
和 valMax
之间。
与其调用外部 awk 来处理短列表,不如使用循环。通过排序(仅调用一次)和打印,此脚本的功能与您的脚本大致相同,但外部调用却少得多:
#!/bin/bash
lineItem5=61 valMin=260 valMax=433
exptVals=( 257 261 265 269 273 277 281 285 289 293 297 \
301 305 309 313 317 321 325 329 333 337 341 \
345 349 353 357 361 365 369 373 377 381 385 \
389 393 397 401 405 409 413 417 421 425 429 \
433 )
for v in $( printf '%3.0f\n' {0..9}{0..9}{0,3,5,7} )
do (( v>=valMin && v<=valMax )) && allVals+=( "$v" )
done
sortVals=( $(printf '%s\n' "${allVals[@]}" "${exptVals[@]}"|sort -nu) )
printf '%s ' "${sortVals[@]}"
现在我们进入您问题的核心。如何:
remove the value ##7 if the sequence ##7 ##8 is encountered
通常的做法是调用 sed
。类似于:
printf '%s ' "${sortVals[@]}" | sed -e 's/\(..7 \)\(..8\)//g'
这会将 ..7 ..8
转换为 ..8
(反向引用 \2)。
然后,您可以添加更多过滤器以进行更多更改。类似于:
printf '%s ' "${sortVals[@]}" |
sed -e 's/\(..7 \)\(..8\)//g' |
sed -e 's/\(..\)3\( ..4\)//g'
echo
这将解决 ..7 ..8
到 ..8
和 ..3 ..4
到 ..2 ..4
项。
但是您的要求是:
but only if the ##3 or ##7 value is not found in my list
见面比较复杂。我们需要使用 grep 扫描所有值,并为每个选项执行不同的代码。一种常用的解决方案是使用 grep:
if printf '%s ' "${sortVals[@]}" | grep -Eq '..3|..7'; then
cmd2=(cat)
else
cmd2=(sed -e 's/\(..2\)\( ..3\)//g')
fi
但这意味着针对每个条件使用 grep 扫描所有值。
创建的命令:cmd2
是一个数组,可以这样使用:
printf '%s ' "${sortVals[@]}" |
sed -e 's/\(..7 \)\(..8\)//g' | "${cmd2[@]}" |
sed -e 's/\(..\)3\( ..4\)//g' | "${cmd4[@]}"
echo
没有 grep
您正在测试的值只是最后一位,可以使用模 10 数学运算轻松提取。并制作 easier/faster 值的测试,我们可以像这样创建一个索引数组:
unset indexVals; declare -A indexVals
for v in "${sortVals[@]}"; do indexVals[$((v%10))]=1; done
这只是一次值扫描,没有调用外部工具,并且大大简化了值测试(例如,对于 ..2
或 ..3
):
(( ${indexVals[2]-0} || ${indexVals[3]-0} ))
包含所有更改的脚本是:
#!/bin/bash
lineItem5=61 valMin=260 valMax=433
exptVals=( 257 261 265 269 273 277 281 285 289 293 297 \
301 305 309 313 317 321 325 329 333 337 341 \
345 349 353 357 361 365 369 373 377 381 385 \
389 393 397 401 405 409 413 417 421 425 429 \
433 )
for v in $( printf '%3.0f\n' {0..9}{0..9}{0,3,5,7} )
do (( v>=valMin && v<=valMax )) && allVals+=( "$v" )
done
sortVals=( $(printf '%s\n' "${allVals[@]}" "${exptVals[@]}" | sort -nu) )
unset indexVals; declare -A indexVals
for v in "${sortVals[@]}"; do indexVals[$((v%10))]=1; done
cmd1=( sed -e 's/\(..7 \)\(..8\)//g' )
(( ${indexVals[2]-0} || ${indexVals[3]-0} )) &&
cmd2=( cat ) ||
cmd2=( sed -e 's/\(..2\)\( ..3\)//g' )
cmd3=( sed -e 's/\(..\)3\( ..4\)//g' )
(( ${indexVals[3]-0} || ${indexVals[7]-0} )) &&
cmd4=( cat ) ||
cmd4=( sed -e 's/\(..6 ..\)7//g' )
printf '%s ' "${sortVals[@]}" | "${cmd1[@]}" | "${cmd2[@]}" |
"${cmd3[@]}" | "${cmd4[@]}" ; echo