Search code examples
inputzsh

How to default to "Y" with Zsh's read -q


For some reason this function always gets "n" as the input when I hit return, even though I would expect it to get an empty string or maybe a newline character. I would expect it to get just about anything EXCEPT "n"

function readtest() {-
  local YorN  # this ensures there is no left-over value in YorN
  #echo -ne "Go ahead?  [Y/n]"; read -q YorN  # this version the same result as the next line
  read -q "YorN?Go ahead?  [Y/n]"
  [[ "$YorN" = "\n" ]] && echo "Matched Newline" # I don't really expect this to return true: it's just here to test
  [[ "$YorN" != "n" ]] && echo "Do the thing!" # I expect this test to return true... this is the weird thing, and central to my question
  echo "## $YorN ##"  # I always get "## n ##"
}

Why is the value of YorN "n"???


Solution

  • This is the expected behavior when using the option -q. Here is the relevant part of the ZSH manual:

    read [ -rszpqAclneE ] [ -t [ num ] ] [ -k [ num ] ] [ -d delim ]
         [ -u n ] [ name[?prompt] ] [ name ... ]
    

    [...]

    -q

    Read only one character from the terminal and set name to ‘y’ if this character was ‘y’ or ‘Y’ and to ‘n’ otherwise. With this flag set the return status is zero only if the character was ‘y’ or ‘Y’. This option may be used with a timeout (see -t); if the read times out, or encounters end of file, status 2 is returned. Input is read from the terminal unless one of -u or -p is present. This option may also be used within zle widgets.

    If you want to have the opposite behavior, i.e. only assume a negative answer if you entered either ‘n’ or ‘N’ and a positive answer otherwise, you can use the option -k 1:

    function readtest {
        local YorN
        read -q "YorN?Go ahead? [Y/n]"
        if [[ ${(U)YorN} == "N" ]] ; then 
            echo "Don't do the thing!"
        else
            echo "Go ahead!" 
        fi
    }
    

    Another option would be to invert the question:

    read -q "YorN? Stop here? [y/N]"