Change variable named in argument to bash function [duplicate]

女生的网名这么多〃 提交于 2021-02-10 18:48:43

问题


In my bash scripts, I often prompt users for y/n answers. Since I often use this several times in a single script, I'd like to have a function that checks if the user input is some variant of Yes / No, and then cleans this answer to "y" or "n". Something like this:

yesno(){
    temp=""
    if [[ "$1" =~ ^([Yy](es|ES)?|[Nn][Oo]?)$ ]] ; then
        temp=$(echo "$1" | tr '[:upper:]' '[:lower:]' | sed 's/es//g' | sed 's/no//g')
        break
    else
        echo "$1 is not a valid answer."
    fi
}

I then would like to use the function as follows:

while read -p "Do you want to do this? " confirm; do # Here the user types "YES"
    yesno $confirm
done
if [[ $confirm == "y" ]]; then
    [do something]
fi

Basically, I want to change the value of the first argument to the value of $confirm, so that when I exit the yesno function, $confirm is either "y" or "n".

I tried using set -- "$temp" within the yesnofunction, but I can't get it to work.


回答1:


You could do it by outputting the new value and overwriting the variable in the caller.

yesno() {
    if [[ "$1" =~ ^([Yy](es|ES)?|[Nn][Oo]?)$ ]] ; then
        local answer=${1,,}
        echo "${answer::1}"
    else
        echo "$1 is not a valid answer." >&2
        echo "$1"  # output the original value
        return 1   # indicate failure in case the caller cares
    fi
}

confirm=$(yesno "$confirm")

However, I'd recommend a more direct approach: have the function do the prompting and looping. Move all of that repeated logic inside. Then the call site is super simple.

confirm() {
    local prompt=$1
    local reply

    while true; do
        read -p "$prompt" reply

        case ${reply,,} in
            y*) return 0;;
            n*) return 1;;
            *)  echo "$reply is not a valid answer." >&2;;
        esac
    done
}

if confirm "Do you want to do this? "; then
    # Do it.
else
    # Don't do it.
fi

(${reply,,} is a bash-ism that converts $reply to lowercase.)




回答2:


You could use the nameref attribute of Bash (requires Bash 4.3 or newer) as follows:

#!/bin/bash

yesno () {
    # Declare arg as reference to argument provided
    declare -n arg=$1

    local re1='(y)(es)?'
    local re2='(n)o?'

    # Set to empty and return if no regex matches
    [[ ${arg,,} =~ $re1 ]] || [[ ${arg,,} =~ $re2 ]] || { arg= && return; }

    # Assign "y" or "n" to reference
    arg=${BASH_REMATCH[1]}
}

while read -p "Prompt: " confirm; do
    yesno confirm
    echo "$confirm"
done

A sample test run looks like this:

Prompt: YES
y
Prompt: nOoOoOo
n
Prompt: abc

Prompt: 

The expressions are anchored at the start, so yessss etc. all count as well. If this is not desired, an end anchor ($) can be added.

If neither expression matches, the string is set to empty.



来源:https://stackoverflow.com/questions/47914584/change-variable-named-in-argument-to-bash-function

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!