Bash «если число в массиве» также учитывает частичные совпадения. Как это решить? - PullRequest
1 голос
/ 10 февраля 2020

Я написал следующий bash код:

NStep=25
NTotal=435
Redone=('375' '400' '425' 'last')
for i in $(seq 0 $NStep $NTotal)
do
    if [[ "${Redone[@]}" =~ "$i" ]]; then
        echo "do something"
    else
        echo "do something else"
    fi
done

Однако для шагов 0, 25 и 75 он вводит оператор if (и что-то делает) тогда как я хочу, чтобы он вводил оператор else (и делал что-то еще). Как я могу сделать свое регулярное выражение более точным c или переписать оператор if?

Спасибо

Ответы [ 4 ]

2 голосов
/ 10 февраля 2020

В bash регулярное выражение не обязательно должно соответствовать всей строке. Вы должны сказать, что число должно быть заключено в пробелы или начало или конец строки:

if [[ "${Redone[@]}" =~ (^| )"$i"( |$) ]]; then
1 голос
/ 10 февраля 2020

Используйте ассоциативный массив, а не индексированный массив. (Также не используйте seq; используйте вместо этого C -стиль для l oop.)

NStep=25
NTotal=435
declare -A Redone=([375]= [400]= [425]= [last]=)
for ((i=0; i<= $NTotal; i+=$NStep))
do
    if [[ -v Redone[$i] ]]; then
        echo "do something"
    else
        echo "do something else"
    fi
done

Ключи ассоциативного массива действуют как набор, а -v (который здесь проверяет, существует ли данный ключ в массиве) действует как оператор членства.


Для версий bash, которые не позволяют использовать -v с именами индексированных массивов ( 4.2 или более ранней версии), вы можете вернуться к проверке, если расширение не установлено.

if [[ "${Redone[$i]+x}" = x ]]

Поскольку вы использовали пустую строку в качестве значения для каждого, ${Redone[$i]+x} будет расширяться до x только когда $i является ключом в массиве.

0 голосов
/ 10 февраля 2020

Есть много способов проверить, содержится ли элемент в массиве:

NStep=25
NTotal=435
Redone=('375' '400' '425' 'last')
for i in $(seq 0 $NStep $NTotal)
do
    if printf "%s\0" "${Redone[@]}" | grep -F -x -q -z "$i"; then
    # or if printf "%s\n" "${Redone[@]}" | grep -F -x -q "$i";
        echo "do something"
    else
        echo "do something else"
    fi
done

Но я думаю, что было бы проще просто отфильтровать элементы, которые вам не интересны:

comm -12 <(printf "%s\n" "${Redone[@]}" | sort) <(seq 0 "$NStep" "$NTotal" | sort)
# outputs 375 400 425

Или вы можете использовать join и пронумеровать строки в seq, прибегнуть к ним и произвести замену:

# this will print 15 lines of 'do something else'
# and 3 lines with 'do something'
# exactly the same as the `for i in ...` loop above
join -22 -a2 -e "do something else" -o2.1,1.1 <(printf "%s\n" "${Redone[@]}" | sort) <(seq 0 $NStep $NTotal | nl -w1 | sort -k2) |
sort -n -k1 |
cut -d' ' -f2- |
sed 's/[0-9]\+/do something/'
0 голосов
/ 10 февраля 2020

Сопоставление регулярных выражений приводит к частичным совпадениям, я предпочитаю более конкретное решение.

NStep=25
NTotal=435
Redone=('375' '400' '425' 'last')
for i in $(seq 0 $NStep $NTotal)
do
  isPresent=0  
  for redone in "${Redone[@]}"
  do
    if [ "${redone}" = "${i}" ]
    then
      isPresent=1
      break
    fi
  done
  if [[ "${isPresent}" -eq "1" ]]; then
        echo "do something"
    else
        echo "do something else"
    fi
done
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...