Bug in select-command

classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Bug in select-command

skynet@top-email.net
It is necessary to refresh the variable $COLUMNS in a script (COLUMNS="tput
cols") otherwise the command "select" will not set the number of columns
correctly for actual screen resolution.

Information: checkwinsize    on

Please try this script-example to see the effect:
#!/bin/bash

### SET VARIABLES ###
        PS3="Select: "


# EXECUTE THE SCRIPT WITH THE NEXT LINE (correctly set number of columns) AND
WITHOUT (just 2 columns) TO SEE THE EFFECT !!!
        COLUMNS="$(tput cols)"


### DEFINE PRE-FUNCTIONS ###
echo() { builtin echo -e "$@" ;}
read() { builtin read -re "$@" ;}
bc() { echo "scale=2;$1" | $(which bc) -l ;}

### DEFINE FUNCTIONS ###
_config-keymap() {
        # Select keymap
                # find keymap list, cut directory path and sort
                        local list_keymaps=( $(find /usr/share/kbd/keymaps/ -type f -name
*.map.gz | grep -Eo [^/]*.map.gz | sort -V) )
                # cut suffixes
                        list_keymaps=( ${list_keymaps[@]%%.*} )

                select locale_keymap in ${list_keymaps[@]}; do
                        if [[ "$REPLY" -ge 1 && "$REPLY" -lt ${#list_keymaps[@]} ]]; then
                                echo ${locale_keymap}; break
                        fi
                done
}

### MAIN PROGRAMM ###
clear
_config-keymap

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug in select-command

Eduardo A. Bustamante López
On Sat, Jun 24, 2017 at 01:46:00PM +0200, [hidden email] wrote:
[...]

>
> ### DEFINE FUNCTIONS ###
> _config-keymap() {
> # Select keymap
> # find keymap list, cut directory path and sort
> local list_keymaps=( $(find /usr/share/kbd/keymaps/ -type f -name
> *.map.gz | grep -Eo [^/]*.map.gz | sort -V) )
> # cut suffixes
> list_keymaps=( ${list_keymaps[@]%%.*} )
>
> select locale_keymap in ${list_keymaps[@]}; do
> if [[ "$REPLY" -ge 1 && "$REPLY" -lt ${#list_keymaps[@]} ]]; then
> echo ${locale_keymap}; break
> fi
> done
> }
>
> ### MAIN PROGRAMM ###
> clear
> _config-keymap
>
I don't have `/usr/share/kbd/keymaps/' in my system.

    dualbus@debian:~$ ls -l /usr/share/kbd/keymaps/
    ls: cannot access '/usr/share/kbd/keymaps/': No such file or directory

From what I can tell, I get these keymaps by installing `console-data'
in my Debian system, although these are installed under `/usr/share/keymaps/',
and have different extensions (e.g.
`/usr/share/keymaps/i386/qwerty/pc110.kmap.gz')

~~~~~

I think this is an easier way to reproduce the problem. I have a terminal
window with the following dimensions:

  dualbus@debian:~$ declare -p COLUMNS LINES
  declare -- COLUMNS="191"
  declare -- LINES="49"

  dualbus@debian:~$ bash --version | head -n1
  GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)

Running the script in `interactive' mode (which triggers calls to
`get_new_window_size' through `get_tty_state' when checkwinsize is
enabled), lays out the options on seven columns:

  dualbus@debian:~$ bash -i script
  $- himB
  cols 191
  1) aaaaaaaaaaaaaaaaaaaa    8) aaaaaaaaaaaaaaaaaaaa  15) bbbbbbbbbbbbbbbbbbbb  22) cccccccccccccccccccc  29) dddddddddddddddddddd  36) eeeeeeeeeeeeeeeeeeee  43) ffffffffffffffffffff
  2) aaaaaaaaaaaaaaaaaaaa    9) bbbbbbbbbbbbbbbbbbbb  16) bbbbbbbbbbbbbbbbbbbb  23) cccccccccccccccccccc  30) dddddddddddddddddddd  37) eeeeeeeeeeeeeeeeeeee  44) ffffffffffffffffffff
  3) aaaaaaaaaaaaaaaaaaaa   10) bbbbbbbbbbbbbbbbbbbb  17) cccccccccccccccccccc  24) cccccccccccccccccccc  31) dddddddddddddddddddd  38) eeeeeeeeeeeeeeeeeeee  45) ffffffffffffffffffff
  4) aaaaaaaaaaaaaaaaaaaa   11) bbbbbbbbbbbbbbbbbbbb  18) cccccccccccccccccccc  25) dddddddddddddddddddd  32) dddddddddddddddddddd  39) eeeeeeeeeeeeeeeeeeee  46) ffffffffffffffffffff
  5) aaaaaaaaaaaaaaaaaaaa   12) bbbbbbbbbbbbbbbbbbbb  19) cccccccccccccccccccc  26) dddddddddddddddddddd  33) eeeeeeeeeeeeeeeeeeee  40) eeeeeeeeeeeeeeeeeeee  47) ffffffffffffffffffff
  6) aaaaaaaaaaaaaaaaaaaa   13) bbbbbbbbbbbbbbbbbbbb  20) cccccccccccccccccccc  27) dddddddddddddddddddd  34) eeeeeeeeeeeeeeeeeeee  41) ffffffffffffffffffff  48) ffffffffffffffffffff
  7) aaaaaaaaaaaaaaaaaaaa   14) bbbbbbbbbbbbbbbbbbbb  21) cccccccccccccccccccc  28) dddddddddddddddddddd  35) eeeeeeeeeeeeeeeeeeee  42) ffffffffffffffffffff
  #? q

Whereas running the same script in `non-interactive' mode results in three
columns.

  dualbus@debian:~$ bash  script
  $- hB
  cols 191
   1) aaaaaaaaaaaaaaaaaaaa  17) cccccccccccccccccccc  33) eeeeeeeeeeeeeeeeeeee
   2) aaaaaaaaaaaaaaaaaaaa  18) cccccccccccccccccccc  34) eeeeeeeeeeeeeeeeeeee
   3) aaaaaaaaaaaaaaaaaaaa  19) cccccccccccccccccccc  35) eeeeeeeeeeeeeeeeeeee
   4) aaaaaaaaaaaaaaaaaaaa  20) cccccccccccccccccccc  36) eeeeeeeeeeeeeeeeeeee
   5) aaaaaaaaaaaaaaaaaaaa  21) cccccccccccccccccccc  37) eeeeeeeeeeeeeeeeeeee
   6) aaaaaaaaaaaaaaaaaaaa  22) cccccccccccccccccccc  38) eeeeeeeeeeeeeeeeeeee
   7) aaaaaaaaaaaaaaaaaaaa  23) cccccccccccccccccccc  39) eeeeeeeeeeeeeeeeeeee
   8) aaaaaaaaaaaaaaaaaaaa  24) cccccccccccccccccccc  40) eeeeeeeeeeeeeeeeeeee
   9) bbbbbbbbbbbbbbbbbbbb  25) dddddddddddddddddddd  41) ffffffffffffffffffff
  10) bbbbbbbbbbbbbbbbbbbb  26) dddddddddddddddddddd  42) ffffffffffffffffffff
  11) bbbbbbbbbbbbbbbbbbbb  27) dddddddddddddddddddd  43) ffffffffffffffffffff
  12) bbbbbbbbbbbbbbbbbbbb  28) dddddddddddddddddddd  44) ffffffffffffffffffff
  13) bbbbbbbbbbbbbbbbbbbb  29) dddddddddddddddddddd  45) ffffffffffffffffffff
  14) bbbbbbbbbbbbbbbbbbbb  30) dddddddddddddddddddd  46) ffffffffffffffffffff
  15) bbbbbbbbbbbbbbbbbbbb  31) dddddddddddddddddddd  47) ffffffffffffffffffff
  16) bbbbbbbbbbbbbbbbbbbb  32) dddddddddddddddddddd  48) ffffffffffffffffffff
  #? q

The script:

  dualbus@debian:~$ cat script
  #!/bin/bash
  shopt -s checkwinsize
 
  options=(
  aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaa
  bbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbb
  cccccccccccccccccccc cccccccccccccccccccc cccccccccccccccccccc cccccccccccccccccccc cccccccccccccccccccc cccccccccccccccccccc cccccccccccccccccccc cccccccccccccccccccc
  dddddddddddddddddddd dddddddddddddddddddd dddddddddddddddddddd dddddddddddddddddddd dddddddddddddddddddd dddddddddddddddddddd dddddddddddddddddddd dddddddddddddddddddd
  eeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeee
  ffffffffffffffffffff ffffffffffffffffffff ffffffffffffffffffff ffffffffffffffffffff ffffffffffffffffffff ffffffffffffffffffff ffffffffffffffffffff ffffffffffffffffffff
  )
 
  echo \$- $-
  echo cols $(tput cols)
  select opt in "${options[@]}"; do
    break
  done


AFAICT, this is intentional. There's another option though. If you read
https://lists.gnu.org/archive/html/help-bash/2015-03/msg00005.html, you'll see
why the following works:

  # seven columns, non-interactive
  dualbus@debian:~$ bash script
  $- hB
  cols 80
  1) aaaaaaaaaaaaaaaaaaaa    8) aaaaaaaaaaaaaaaaaaaa  15) bbbbbbbbbbbbbbbbbbbb  22) cccccccccccccccccccc  29) dddddddddddddddddddd  36) eeeeeeeeeeeeeeeeeeee  43) ffffffffffffffffffff
  2) aaaaaaaaaaaaaaaaaaaa    9) bbbbbbbbbbbbbbbbbbbb  16) bbbbbbbbbbbbbbbbbbbb  23) cccccccccccccccccccc  30) dddddddddddddddddddd  37) eeeeeeeeeeeeeeeeeeee  44) ffffffffffffffffffff
  3) aaaaaaaaaaaaaaaaaaaa   10) bbbbbbbbbbbbbbbbbbbb  17) cccccccccccccccccccc  24) cccccccccccccccccccc  31) dddddddddddddddddddd  38) eeeeeeeeeeeeeeeeeeee  45) ffffffffffffffffffff
  4) aaaaaaaaaaaaaaaaaaaa   11) bbbbbbbbbbbbbbbbbbbb  18) cccccccccccccccccccc  25) dddddddddddddddddddd  32) dddddddddddddddddddd  39) eeeeeeeeeeeeeeeeeeee  46) ffffffffffffffffffff
  5) aaaaaaaaaaaaaaaaaaaa   12) bbbbbbbbbbbbbbbbbbbb  19) cccccccccccccccccccc  26) dddddddddddddddddddd  33) eeeeeeeeeeeeeeeeeeee  40) eeeeeeeeeeeeeeeeeeee  47) ffffffffffffffffffff
  6) aaaaaaaaaaaaaaaaaaaa   13) bbbbbbbbbbbbbbbbbbbb  20) cccccccccccccccccccc  27) dddddddddddddddddddd  34) eeeeeeeeeeeeeeeeeeee  41) ffffffffffffffffffff  48) ffffffffffffffffffff
  7) aaaaaaaaaaaaaaaaaaaa   14) bbbbbbbbbbbbbbbbbbbb  21) cccccccccccccccccccc  28) dddddddddddddddddddd  35) eeeeeeeeeeeeeeeeeeee  42) ffffffffffffffffffff
  #? ^C
 
  dualbus@debian:~$ cat script
  #!/bin/bash
  shopt -s checkwinsize
 
  options=(
  aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaa
  bbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbb
  cccccccccccccccccccc cccccccccccccccccccc cccccccccccccccccccc cccccccccccccccccccc cccccccccccccccccccc cccccccccccccccccccc cccccccccccccccccccc cccccccccccccccccccc
  dddddddddddddddddddd dddddddddddddddddddd dddddddddddddddddddd dddddddddddddddddddd dddddddddddddddddddd dddddddddddddddddddd dddddddddddddddddddd dddddddddddddddddddd
  eeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeee
  ffffffffffffffffffff ffffffffffffffffffff ffffffffffffffffffff ffffffffffffffffffff ffffffffffffffffffff ffffffffffffffffffff ffffffffffffffffffff ffffffffffffffffffff
  )
 
  echo \$- $-
  echo cols $(tput cols)
  /bin/true # THIS
  select opt in "${options[@]}"; do
    break
  done

For some reason though, the following fails to update the value of COLUMNS:

  dualbus@debian:~$ cat script
  #!/bin/bash
  shopt -s checkwinsize
 
  options=(
  aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaa
  bbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbb
  cccccccccccccccccccc cccccccccccccccccccc cccccccccccccccccccc cccccccccccccccccccc cccccccccccccccccccc cccccccccccccccccccc cccccccccccccccccccc cccccccccccccccccccc
  dddddddddddddddddddd dddddddddddddddddddd dddddddddddddddddddd dddddddddddddddddddd dddddddddddddddddddd dddddddddddddddddddd dddddddddddddddddddd dddddddddddddddddddd
  eeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeee
  ffffffffffffffffffff ffffffffffffffffffff ffffffffffffffffffff ffffffffffffffffffff ffffffffffffffffffff ffffffffffffffffffff ffffffffffffffffffff ffffffffffffffffffff
  )
 
  echo \$- $-
  echo cols $(tput cols)
  command true # this should trigger?
  select opt in "${options[@]}"; do
    break
  done

So the trick is to run an external command in foreground. To see why this
works, read:
http://git.savannah.gnu.org/cgit/bash.git/tree/jobs.c?h=devel#n3390

--
Eduardo Bustamante
https://dualbus.me/

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug in select-command

Eduardo A. Bustamante López
On Sat, Jun 24, 2017 at 9:46 AM, Eduardo A. Bustamante López
<[hidden email]> wrote:
[...]
> For some reason though, the following fails to update the value of COLUMNS:
[...]
>   echo \$- $-
>   echo cols $(tput cols)
>   command true # this should trigger?
>   select opt in "${options[@]}"; do
>     break
>   done

Bah, ignore this remark. `command true' will run the builtin `true'.
The rest of my email should be accurate though.

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug in select-command

Eduardo A. Bustamante López
Chet:

I think the patch below could be a useful addition, to make the behavior
in this case a little bit less surprising.


*** /tmp/BdLnYa_shopt.def 2017-06-24 10:21:15.029707643 -0500
--- builtins/shopt.def 2017-06-24 10:17:00.540600773 -0500
***************
*** 134,139 ****
--- 134,140 ----
  static int shopt_set_complete_direxpand __P((char *, int));
  #endif
 
+ static int shopt_set_check_window_size __P((char *, int));
  static int shopt_set_debug_mode __P((char *, int));
 
  static int shopt_login_shell;
***************
*** 164,170 ****
  #if defined (JOB_CONTROL)
    { "checkjobs", &check_jobs_at_exit, (shopt_set_func_t *)NULL },
  #endif
!   { "checkwinsize", &check_window_size, (shopt_set_func_t *)NULL },
  #if defined (HISTORY)
    { "cmdhist", &command_oriented_history, (shopt_set_func_t *)NULL },
  #endif
--- 165,171 ----
  #if defined (JOB_CONTROL)
    { "checkjobs", &check_jobs_at_exit, (shopt_set_func_t *)NULL },
  #endif
!   { "checkwinsize", &check_window_size, shopt_set_check_window_size },
  #if defined (HISTORY)
    { "cmdhist", &command_oriented_history, (shopt_set_func_t *)NULL },
  #endif
***************
*** 593,598 ****
--- 594,606 ----
  #endif
 
  static int
+ shopt_set_check_window_size (char *option_name, int mode)
+ {
+   get_new_window_size (0, (int *)0, (int *)0);
+   return (0);
+ }
+
+ static int
  set_compatibility_level (option_name, mode)
       char *option_name;
       int mode;

--
Eduardo Bustamante
https://dualbus.me/

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug in select-command

Chet Ramey
In reply to this post by skynet@top-email.net
On 6/24/17 7:46 AM, [hidden email] wrote:
> It is necessary to refresh the variable $COLUMNS in a script (COLUMNS="tput
> cols") otherwise the command "select" will not set the number of columns
> correctly for actual screen resolution.

So you are saying that the value of COLUMNS passed in the environment is
not correct? Or that it's not present?

When the shell isn't interactive, it relies on the value passed in the
environment.  It's not the shell's role to second-guess the environment
value and change it regardless of whather or not it's "correct".

If the value doesn't appear in the environment, it's the programmer's
responsibility to make sure that the correct value is set.

--
``The lyf so short, the craft so long to lerne.'' - Chaucer
                 ``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, UTech, CWRU    [hidden email]    http://cnswww.cns.cwru.edu/~chet/

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug in select-command

Chet Ramey
In reply to this post by Eduardo A. Bustamante López
On 6/24/17 10:46 AM, Eduardo A. Bustamante López wrote:

> I think this is an easier way to reproduce the problem. I have a terminal
> window with the following dimensions:
>
>   dualbus@debian:~$ declare -p COLUMNS LINES
>   declare -- COLUMNS="191"
>   declare -- LINES="49"

You haven't exported these.  If you had, the subshell started to run the
script would have the correct values.

--
``The lyf so short, the craft so long to lerne.'' - Chaucer
                 ``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, UTech, CWRU    [hidden email]    http://cnswww.cns.cwru.edu/~chet/

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug in select-command

Eduardo A. Bustamante López
On Sat, Jun 24, 2017 at 01:17:23PM -0400, Chet Ramey wrote:
[...]
> You haven't exported these.  If you had, the subshell started to run the
> script would have the correct values.

Hm. I think this may be a documentation / usability problem.

The manual states the following:

  COLUMNS
         Used  by the select compound command to determine the terminal width
         when printing selection lists.  *Automatically set if the checkwinsize
         option is enabled* or in an interactive shell upon receipt of a
         SIGWINCH.

  [...]

  checkwinsize
          If set, bash checks the window size after each command and, if
          necessary, updates the values of LINES and COLUMNS.

Furthermore, POSIX [1] says the following:

  COLUMNS
          This variable shall represent a decimal integer >0 used to indicate
          the user's preferred width in column positions for the terminal
          screen or window; see Column Position. If this variable is unset or
          null, the implementation determines the number of columns,
          appropriate for the terminal or window, in an unspecified manner.
          When COLUMNS is set, any terminal-width information implied by TERM
          is overridden. Users and conforming applications should not set
          COLUMNS unless they wish to override the system selection and produce
          output unrelated to the terminal characteristics.

          Users *should not need to set this variable in the environment*
          unless there is a specific reason to override the implementation's
          default behavior, such as to display data in an area arbitrarily
          smaller than the terminal or window.

So, from the manual alone, I know the following:

- `select' uses the COLUMNS variable to determine the terminal width
- the value of COLUMNS is automatically set if the `checkwinsize' option is
  enabled (** restrictions apply).
- bash checks the window size after each command

I have the following problems with the documentation:

- It seems to imply that it's set automatically *when* `checkwinsize' is
  enabled. A careful reading uncovers that this is not true though, and that
  you have to run a command for the window size to be checked.
- It doesn't mention that the window size is NOT checked after *builtin*
  commands.
- POSIX recommends against exporting the COLUMNS variable.

I know that I can get the desired behavior by either:

- Running the script under an interactive shell
- Setting `checkwinsize' and running a dummy external command to trigger the
window size check.
- Exporting the COLUMNS variable from the parent interactive shell.

I think that's too much hassle, when bash could easily:

- If the `select' statement is used and `checkwinsize' is set and
  `COLUMNS' is unset, then call `get_new_window_size'
- Call `get_new_window_size' when `checkwinsize' is enabled.

[1] http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03

--
Eduardo Bustamante
https://dualbus.me/

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug in select-command

Chet Ramey
In reply to this post by skynet@top-email.net
On 6/24/17 7:46 AM, [hidden email] wrote:
> It is necessary to refresh the variable $COLUMNS in a script (COLUMNS="tput
> cols") otherwise the command "select" will not set the number of columns
> correctly for actual screen resolution.
>
> Information: checkwinsize    on

I can't see anywhere in the script where you set the checkwinsize option,
so I can't see what you think the effect should be of having it set in
the parent shell.

--
``The lyf so short, the craft so long to lerne.'' - Chaucer
                 ``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, UTech, CWRU    [hidden email]    http://cnswww.cns.cwru.edu/~chet/

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug in select-command

Chet Ramey
In reply to this post by Eduardo A. Bustamante López
On 6/24/17 10:46 AM, Eduardo A. Bustamante López wrote:

> I think this is an easier way to reproduce the problem. I have a terminal
> window with the following dimensions:
>
>   dualbus@debian:~$ declare -p COLUMNS LINES
>   declare -- COLUMNS="191"
>   declare -- LINES="49"
>
>   dualbus@debian:~$ bash --version | head -n1
>   GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
>
> Running the script in `interactive' mode (which triggers calls to
> `get_new_window_size' through `get_tty_state' when checkwinsize is
> enabled), lays out the options on seven columns:

Yes, bash is actually doing more work here than it should. The reason for
the calls to get_tty_state is if a program terminates prematurely and
leaves the terminal in a state where interactive input is inconvenient
(noecho mode, for example). If bash isn't reading input from the terminal,
this isn't strictly necessary. But for now, it works.


> Whereas running the same script in `non-interactive' mode results in three
> columns.

Yes, there's no reason to worry about tty settings, so the shell relies on
the environment for the value of COLUMNS.  If it's not there, or doesn't
have a sane value, select defaults to 80.

> So the trick is to run an external command in foreground. To see why this
> works, read:
> http://git.savannah.gnu.org/cgit/bash.git/tree/jobs.c?h=devel#n3390

Yes, since the point is to protect interactive shell input from badly-
behaved external programs that can modify the terminal settings.  The
shell knows what it does to the terminal and how to undo it.

Chet
--
``The lyf so short, the craft so long to lerne.'' - Chaucer
                 ``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, UTech, CWRU    [hidden email]    http://cnswww.cns.cwru.edu/~chet/

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug in select-command

Chet Ramey
In reply to this post by Eduardo A. Bustamante López
On 6/24/17 1:38 PM, Eduardo A. Bustamante López wrote:
> On Sat, Jun 24, 2017 at 01:17:23PM -0400, Chet Ramey wrote:
> [...]
>> You haven't exported these.  If you had, the subshell started to run the
>> script would have the correct values.
>
> Hm. I think this may be a documentation / usability problem.

It's a documentation problem, since the documentation doesn't completely
reflect the behavior.

> The manual states the following:
>
>   COLUMNS
> Used  by the select compound command to determine the terminal width
> when printing selection lists.  *Automatically set if the checkwinsize
> option is enabled* or in an interactive shell upon receipt of a
>          SIGWINCH.

Yes, and the checkwinsize description has the details of how it does that.

>   [...]
>
>   checkwinsize
>  If set, bash checks the window size after each command and, if
>  necessary, updates the values of LINES and COLUMNS.

This should be after each external (non-builtin) command.

> Furthermore, POSIX [1] says the following:
>
>   COLUMNS
>  This variable shall represent a decimal integer >0 used to indicate
>  the user's preferred width in column positions for the terminal
>  screen or window; see Column Position. If this variable is unset or
>  null, the implementation determines the number of columns,
>  appropriate for the terminal or window, in an unspecified manner.

This is Posix-speak for "we don't know how it does it, but we assume the
appropriate value is put into COLUMNS and exported somehow," since "the
implementation" doesn't mean "the shell".


>  When COLUMNS is set, any terminal-width information implied by TERM
>  is overridden. Users and conforming applications should not set
>  COLUMNS unless they wish to override the system selection and produce
>  output unrelated to the terminal characteristics.
>
>  Users *should not need to set this variable in the environment*
>  unless there is a specific reason to override the implementation's
>  default behavior, such as to display data in an area arbitrarily
>  smaller than the terminal or window.

In this case, the implementation's default behavior is to not export
COLUMNS or, apparently, set it in non-interactive shells.

>
> So, from the manual alone, I know the following:
>
> - `select' uses the COLUMNS variable to determine the terminal width
> - the value of COLUMNS is automatically set if the `checkwinsize' option is
>   enabled (** restrictions apply).
> - bash checks the window size after each command

So it's a documentation problem concerning what "each command" means.

> I have the following problems with the documentation:
>
> - It seems to imply that it's set automatically *when* `checkwinsize' is

That's not what it says.  It says "if", not "when".  The description of
`checkwinsize' should describe the circumstances under which it acts.

> - It doesn't mention that the window size is NOT checked after *builtin*
>   commands.

And that is the problem.

> - POSIX recommends against exporting the COLUMNS variable.

No, it doesn't. It says that it should usually not be necessary.  If you
find that your implementation doesn't make the value available, you have
the option to export it.
--
``The lyf so short, the craft so long to lerne.'' - Chaucer
                 ``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, UTech, CWRU    [hidden email]    http://cnswww.cns.cwru.edu/~chet/

Loading...