Создать общую функцию, чтобы задать вопрос и проверить, пуст ли ответ

Я создаю приложение-оболочку, которое задает много вопросов, и использую read -p "" несколько раз. Проблема в том, что я также хочу проверить, пусты ли ответы. Итак, мне захотелось создать универсальную функцию, чтобы спрашивать и проверять, пуста она или нет. Если это так, вызовите саму функцию рекурсивно, пока пользователь что-то не предоставит. Когда я «зафиксировал» имя переменной, скажем, «база данных пользователя», все заработало на удивление хорошо. Следует за объявлением и использованием функции:

ask() {
    read -p "$1" $2

    if [[ -z $userdatabase ]]; then
        echo Empty is not allowed
        ask "$1" $2
    else
        echo $userdatabase
    fi
}

ask "Provides the user database: " userdatabase

Конечно, я не хочу указывать «базу данных пользователя» в качестве имени переменной для всех вопросов, которые будет задавать приложение. Итак, я заметил, что мне нужна своего рода «динамическая» переменная. Если позволить мыслить немного более динамично, она становится такой:

ask() {
    read -p "$1" $2

    if [[ -z $$var ]]; then
        echo Empty is not allowed
        ask "$1" $2
    else
        echo $$var
    fi
}

ask "Provides the user database: " $var

Но когда я использую эту утилиту, я получаю что-то вроде SOMENUMBERvar. Очевидно, я неправильно использую «динамическую переменную» в оболочке.

Итак, как мне создать функцию, которая получает вопросник и имя переменной, которое будет заполнено переменной из команды read -p ?

0
07.03.2017, 18:25
4 ответа

Я хотел бы поделиться тем, что обнаружил с опозданием. Я пытался добавить еще один шаг в проверку ответа: проверить, существует ли путь, и не мог приспособить для этого решение Ракеша Шармы. Наконец, я нашел именно то, что искал, - способ работы с "динамической переменной", и реальный способ сделать это - использовать ${!var}. Вот окончательная версия моей функции и ее использование:

ask_validate() {

    read -p "$1" $2

    if [ -z ${!2} ]; then
        echo Empty answer is not allowed.
        ask_validate "$1" $2
        return
    fi

    if ! [ -d ${!2} ]; then
        echo You need to provides an existing path
        ask_validate "$1" $2
        return
    fi

    echo The var name is: $2
    echo The var value is: ${!2}
}

ask_validate "Please, provides the host path: " host_path
ask_validate "Please, provides the virtual host configuration path: " virtualhost_path

echo The host path is $host_path
echo The virtual host configuration path is $virtualhost_path
0
28.01.2020, 04:48

Начните с простейшего возможного случая.Создание функции:

f() { read -p "Type smthng: " $1 ; }

Вызов функции, присвоение переменной $ p для удержания ее ввода, затем отображение ввода после выхода функции:

f p ; echo $p

It запросов, я набираю «woof», и echo $ p выводит то же самое:

Type smthng: woof
woof

Один из способов проверить переменную - заключить эту функцию в другую:

g() { unset $1 ; until f "$1" && eval [ \"\$"$1"\" ] ; do echo wrong ; done ; }

Сложность заключается в том, что OP хочет чтобы назначить имя переменной как параметр функции, для анализа которого я использую "evil" eval . Передача имен переменных в качестве параметров функции выполняется редко (или требуется), но здесь показан один из способов сделать это.

Протестируйте:

g p ; echo $p

Появляется запрос, я нажимаю Enter , выводится сообщение об ошибке; затем во втором приглашении я набираю «foo»:

Type smthng: 
wrong
Type smthng: foo
foo

Эта часть кода OP не будет работать:

if [[ -z $$var ]]; then

$$ - это переменная bash , которая возвращает текущий PID:

 man bash | grep -A 28 "Special Parameters$"  | sed -n '1,3p;28,$p'
   Special Parameters
       The shell treats several parameters specially.  These  parameters  may  only
       be referenced; assignment to them is not allowed.
       $      Expands to the process ID of the shell.  In a () subshell, it expands
              to the process ID of the current shell, not the subshell.
0
28.01.2020, 04:48
ask() {
   # $1 ---> message to user
   # $2 ---> "VARIABLE NAME" in which read will store response

   case ${2-} in
      '' | *[!a-zA-Z0-9_]* | [0-9]* )
          echo >&2 "Oops you've selected an invalid variable name: \`$2'"
          exit 1;;
   esac

   read -p "$1" "$2"

   eval "
      case \${$2:++} in
         '' )
               echo >&2 'Empty response not allowed.'
               ask '$1' '$2';;
         * )
            echo \"\${$2}\";;
      esac
   "
}

VarName=${1-'humpty_dumpty'}
ask "Provide the user database: " "$VarName"
0
28.01.2020, 04:48

Итак, я подумал о создании общей функции, чтобы спрашивать и проверять, пуста она или нет.

Это, наверное, хорошая идея.

Если это так, вызовите саму функцию рекурсивно, пока пользователь что-то не предоставит.

Это ... не такая уж хорошая идея. Просто используйте петлю.

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

Кроме того, вместо того, чтобы передавать имя переменной в качестве аргумента функции, вы можете просто выполнить присваивание в основной программе:

ask() {
        read -p "$1: " a
        while [ -z "$a" ] ; do
                echo "Please give a value" >&2
                read -p "$1: " a
        done
        echo "$a"
}

var=$(ask "please enter some value")    
echo "you gave $var"

Конечно, теперь мы должны перенаправить напоминание на stderr, потому что stdout назначен на переменная. Это то же самое, что read делает с подсказкой. Это также работает, скажем, в тире , который, похоже, не поддерживает косвенную ссылку $ {! X} .

0
28.01.2020, 04:48

Теги

Похожие вопросы