Search code examples
bashenvironment-variablescompgen

Why does compgen behave differently when called which bash -ilc


If I call compgen on bash directly with an environment variable pointing to a directory, it completes paths inside that directory. This can include environment variables if they are no resolved before calling compgen.

> cd /tmp
> mkdir files
> touch files/aa files/ab
> export MY_FILES=/tmp/files
> echo "$MY_FILES/"
/tmp/files
> compgen -v -A file -- "$MY_FILES/"  # not what I want
/tmp/files/ab
/tmp/files/aa
> echo "\$MY_FILES/"
$MY_FILES/
> compgen -v -A file -- "\$MY_FILES/" # this is what I want
$MY_FILES/ab
$MY_FILES/aa

This works fine when I execute the command but doesn't work when the command is called indirectly (not even in a login shell)

> bash -ilc 'echo "\$MY_FILES/"; compgen -v -A file -- "\$MY_FILES/"'
$MY_FILES/
> echo $?
1

You can tell from the echo command that the escaping works as intended, so this is not an issue with how the string is escaped.

Likewise, putting the line in a script and calling the script doesn't work:

> cat run-compgen
#!/bin/bash -il

echo "\$MY_FILES/"
compgen -v -A file -- "\$MY_FILES/"
echo $?

> ./run-compgen
$MY_FILES/
1

Unsuccessful Solutions

  1. You need a login shell: I tried login and interactive shells, both did not resolve the issue. I now changed the question so this is already included in the examples.
  2. You should not escape the variable: If I do not escape the variable, the command prints something, but not what I want. Please have a look at the first code box in my question. It lists two variants of calling the command. I want the second type of output, not the first. I also thought that \$ might be the wrong way of escaping $, so I tried with 2, 3, or 4 backslashes without success.
  3. The variable is not known in the nested shell: I tried bash -c env and it showed the variable.
  4. This is related to your environment: I tried to exclude this option by also testing inside a clean docker container.
> docker run -it ubuntu bash # start a clean Ubuntu container.
# Then inside the container:
> cd /tmp
> mkdir files
> touch files/aa files/ab
> export MY_FILES=/tmp/files
> bash -ilc 'compgen -v -A file -- "\$MY_FILES/"'

Solution

  • This was fixed in bash 5.3. The temporary solution is to call bind at the beginning of the command, which works with any shell (login, interactive, normal) but generates a warning unless used with an interactive shell:

    bash -ic 'bind; compgen -v -A file -- "\$MY_FILES/"'
    

    (https://savannah.gnu.org/support/index.php?111125)