Skip to content

Aborting a completion attempt leaves set -o noglob in effect #786

Open
@abbeyj

Description

@abbeyj

Describe the bug

Certain completions turn off globbing with set -o noglob, perform some operation, and then attempt to reset globbing back to its original state. See for example

reset=$(shopt -po noglob)
set -o noglob
toks=($(compgen -d -- "${cur-}"))
IFS=' '
$reset
IFS=$'\n'
but this pattern is used in more than one place.

Unfortunately if the user presses Ctrl+C in the middle of this then the reset code is never run and the shell is left with set -o noglob in effect. This will likely be very disconcerting for the user. For instance, running ls * reports ls: cannot access *: No such file or directory. That's the message you'd normally expect to see if the directory is empty but now you'll see it reported for all directories, giving the impression that all of your files have been deleted.

A user could recover from this by running set +o noglob but they'd have to realize what was wrong first. I'm assuming in most cases people will just think their terminal is "broken" and be forced to close it and open a new one.

See also #691 .

To reproduce

You'll need a directory somewhere that's very slow to enumerate. One option would be to mount an NFS directory from some slow, distant machine. Another would be to create a local directory with an absurdly large number of files in it and have a cold cache (possibly something like echo 3 > /proc/sys/vm/drop_caches might help on Linux). Or maybe https://serverfault.com/a/954175. I can personally reproduce this with a large directory on NFS.

# Verify that globbing is turned on to start
$ shopt -po noglob
set +o noglob

$ cd /path/to/your/directory/<TAB><Ctrl+C>

# Globbing is now unexpectedly turned off
$ shopt -po noglob
set -o noglob

Expected behavior

The noglob setting should retain the previous value.

Versions (please complete the following information)

  • Operating system name/distribution and version: CentOS Linux release 7.9.2009 (Core)
  • bash version, echo "$BASH_VERSION": 4.2.46(2)-release
  • bash-completion version, (IFS=.; echo "${BASH_COMPLETION_VERSINFO[*]}"): This command outputs "2.11.0", but I'm actually using git commit 36ceb27.

Additional context

Maybe this could be handled by running the compgen in a subshell so that the setting would not have to be remembered and then explicitly restored?

Debug trace

I'm going to avoid including entire thing for now since it is large and I don't think it will help explain the problem. If you still need it, please tell me and I'll send the full one. It ends with this, the compgen having been interrupted by Ctrl+C:

+ local -a toks
+ local reset arg=-d
+ [[ -d == -d ]]
++ shopt -po noglob
+ reset='set +o noglob'
+ set -o noglob
+ toks=($(compgen -d -- "${cur-}"))
++ compgen -d -- /path/to/your/directory/
^C
$

I can't reproduce the problem on a different system that uses bash 5.1. I believe that's because of a recent change to bash, introduced in v5.1, that masks SIGINT during the execution of compgen: https://github.com/bminor/bash/blob/9439ce094c9aa7557a9d53ac7b412a23aa66e36b/subst.c#L6542. This makes the code in bash-completion work as expected but has the downside that you can't abort the completion and have to wait for it to finish no matter how long it takes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions