Функция Bash с циклом for и вложенным оператором if, дающая необычный результат

Функция Bash с циклом for и вложенным оператором if, дающая необычный результат

У меня есть следующая функция bash, которая меня сбивает с толку. Если я ввожу следующее в поля zenity...

идентификатор сотрудника = 2 идентификатор категории = 3

Я получаю следующее: 2 3

если...

идентификатор сотрудника = идентификатор категории = 3

После того, как открывается второе окно zenity, я ввожу 2 и получаю следующее: 2 3

Однако, когда я вхожу

идентификатор_сотрудника = 2 идентификатор_категории =

Никакого дополнительного окна Zenity не открывается, и я получаю следующее: 2

На самом деле я хочу получить результат 2,3 после проведения тестов.

Кто-нибудь знает, в чем тут проблема?

#!/bin/bash


num(){
        emp=$(echo "$1" | awk -F, -v  OFS=, '{print $1 "," $2}')

        IFS=, read -ra array1 <<<"$emp"
        
        p=$(for i in "${array1[@]}"
        
        do
                
                if [[ "${i}" =~ ^[0-9]+$ ]]; then

                        out="${i}"
                elif
                        [[ "${i}" = NULL ]]; then

                        out="${i}"


                else   local var2

                        until [[ ${var2} =~ ^[0-9]+$ ]] || [[ ${var2} = NULL ]]; do

                                var2="$(zenity --forms --title="table salaries_wages" --text "Add a number"  --separator="," \
                                --add-entry="WARNING! You either forgot to enter or didn't enter a number. Please enter a valid number: ")"

                         done

                                out="${var2}"

        fi

        echo "$out"
        
done)
      
        echo "$p"
}

input="$(zenity --forms --title="table salaries_wages" --text="Add a new salaries_wages entry" --separator="," \
        --add-entry="ENTER employeeid: " \
        --add-entry="ENTER categoryid: ")"

num "$input"

решение1

что здесь не так?

Ваше предположение о том, как это readработает, отличается от того, как это readработает на самом деле. Запустите этот код в Bash:

how_many () { IFS=, read -ra array1 <<<"$1"; echo "${#array1[@]}"; }
how_many "2,3"
how_many ",3"
how_many "2,"

Вы получите 2, 2, 1. Последний номер выделяется. Это означает, что конечный разделитель ( ,в данном случае) обрабатывается readскорее как терминатор: пустое поле после него не считывается в массив, и массив оказывается на один элемент короче. Если это происходит в вашем коде, то for i in "${array1[@]}"цикл будет запущен только для первого поля.

Решением может быть введениедополнительный ,как завершающий терминатор намеренно. Тогда readон никогда не будет читать третье поле, но всегда будет читать два поля (даже если второе пустое). Посмотрите на разницу, когда я добавляю дополнительный ,:

how_many () { IFS=, read -ra array1 <<<"$1,"; echo "${#array1[@]}"; }
how_many "2,3"
how_many ",3"
how_many "2,"
how_many ","

Выход — 2каждый раз.

Чтобы исправить код таким образом, используйте <<<"$emp,"вместо <<<"$emp".


Если вы исправите код, то он будет вести себя неправильно, когда elseблок будет запущен более одного раза (т. е. когда оба поля изначально недействительны); потому что выповторное использование var2переменнойснова.

Я полагаю, вы избегали local var2этого, но localделали переменную локальной в функции, а не в elseблоке или в одной итерации цикла for. Вы повторно используете var2в том же экземпляре функции num.

Вы вызываете функцию один раз. Внутри она var2всегда одна и та же var2, localтолько отличает ее от любой var2за пределами этого самого вызова функции. Если вы использовали var2за пределами функции, то тот, что в функции, будет отличаться. Если вы вызывали numбольше одного раза, каждый вызов будет использовать свой собственный different var2. Ничего из этого не происходит. Вы вызываете функцию один раз и повторно используете переменнуютам. Если два поля недействительны, переменная используется для первого поля, а затемповторно использованныйдля второго поля.

Но если вы перестроите свой код так, чтобы некоторая функция (например validate, ) вызывалась изнутри цикла:

for i in "${array1[@]}"; do validate "$i"; …

и использовать local var2в validateфункции, то var2при каждом вызове функции будет отличаться. Вот как localможет помочь. В каждом цикле validateбудет вызван заново, его локальная переменная(ые) будет инициализирована заново без какой-либо связи с другими переменными(ыми) с тем же именем(ями). Однако вы все равно сможете повторно использовать локальную переменнуювнутрифункцию таким образом, что это что-то ломает (аналогично тому, как вы сейчас повторно используете var2функцию num).

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


Примечание p=$(stuff); echo "$p"почти эквивалентно echo "$(stuff)", которое почти всегда должно быть просто stuff. Пожалуйста, прочтитеэтот ответгде подробно рассказывается о var=$(stuff); echo "$var".

Связанный контент