1

In my script after I've cded inside a folder I check if direnv command is available in the system and if a .envrc file has been already created. If so, I'm generating a python virtual env with:

direnv allow
eval "$(direnv export bash)"

For some reasons, the VIRTUAL_ENV variable value gets a dollar sign in front and I can't manage it as I want afterwards. Here's the full function that does this.

python_venv_binary() {
    if [ `command -v direnv &>/dev/null;` ] && [ -f ".envrc" ]; then
        direnv allow
        eval "$(direnv export bash)"
        echo $VIRTUAL_ENV
        # prints $/home/whatever/whatever/whatever/.direnv/python-3.8.5
        $VIRTUAL_ENV/bin/python
    fi
}

python_venv_binary
# $/home/whatever/whatever/whatever/.direnv/python-3.8.5/bin/python: not found

I'm running the script as sh script.sh. What's strange is that running the same commands manually correctly behaves.

With set -x here's the output (I've posted what I think is relevant with the case). Oddly the exports gets all a $ in front which appears to indicate the issue.

+ command -v direnv
+ [ /usr/bin/direnv ]
+ [ -f .envrc ]
+ direnv allow
+ direnv export bash
direnv: loading ~/Development/kitty/.envrc
direnv: export +VIRTUAL_ENV ~PATH
+ eval export DIRENV_DIR=$'-/home/bernardo/Development/kitty';export VIRTUAL_ENV=$'/home/bernardo/Development/kitty/.direnv/python-3.8.5';export DIRENV_DIFF=$'eJzs1k1vmzAYB_Dv4nMSorgJBGkHHNIApSEJL-24IEKcQDCGGIe8VPnuU9utndLtsmmHqb7hx_7_hCxLz_MEKqA-gZnmGUAFUloWWFpiRmO2KqUOKZOYSMuMqlc7L6V9zaS3E1IRs03JoxRjEiUku04IS1jCEpawhPU31nuw_gi9Lt83Xj5el2-1TVzg-ufc90JN4-r5FLi0AH3uibq5GE-DSDcXQAXtq1_ScYNJWRWYcinPOD-B1o_Ag-aNjLELVICtY_dc3zqprs3RHsHBY3GeywmfnynrJq6seEhfH-6O_u39Li7PjUy73mNbt4eRtaU7n4dbt0u0Xr6zGz9wNsiCVh_KGsSEL9uDr2zIpk6_uMNuwRjazuKmmSG0bxsZpMpDOewVthHo0RFHxGn2_skdBFm-4VZch0ruhKvZJHPze5NAYxRM0GrUd9e8H_IULRNrelMfUBbCYDAZIMPlrDYrhzhrOegpwxRuIW66tqbpUXQ63HiO-QW0fjNEfLgpqbPKGKaNVJ14WtI27Cid_q8eyv_wHIUlLGEJS1ifw_r340cLBObC8zU7Gk-DP22n4HL5FgAA___9FMRg';export DIRENV_WATCHES=$'eJx0zsFOhDAQBuB36XmzQ7ctQznr0cS78TBDfwKxUFMqaozv7n0TX-DL9_JjnqUtZjS0lA2kqLvUVOgBJ3J537A3eltb-6Yr9rNO5mKeSmrrBjPavvPBBu-Hi3n8Wo92mLHVD_xe_lOvuUyS6VikgtJasZ8kOZdPGiSkMIl3HCVGBdC5Sft5ZthBbcJN4swBiZ3V6G6BHStrsIpOlOf7V289h3j3ev0LAAD__yw4TOI='
+ export DIRENV_DIR=$-/home/bernardo/Development/kitty
+ export VIRTUAL_ENV=$/home/bernardo/Development/kitty/.direnv/python-3.8.5
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
Barnercart
  • 1,523
  • 1
  • 11
  • 23
  • 1
    `set -x` is your friend -- and ours as well, to provide enough information to have a place to start debugging this. – Charles Duffy Nov 05 '20 at 22:18
  • 1
    BTW, `printf '%q\n' "$VIRTUAL_ENV"` (with the quotes) provides a more trustworthy representation of the value than `echo $VIRTUAL_ENV` does. See f/e [I just assigned a variable, but `echo $variable` prints something else!](https://stackoverflow.com/questions/29378566/i-just-assigned-a-variable-but-echo-variable-shows-something-else) -- though `%q` is a ksh/zsh/bashism, and thus isn't guaranteed to work with `sh`. – Charles Duffy Nov 05 '20 at 22:19
  • 1
    Also, I don't know why you'd use `[ ]` for testing `command -v direnv`. Just `if command -v direnv >/dev/null 2>&1 && [ -f .envrc ]; then` is going to be more reliable. – Charles Duffy Nov 05 '20 at 22:20
  • 1
    (`command -v` is a more portable alternative to `type`, but using `&>` -- as bash-only syntax -- completely throws away any portability benefits you may have otherwise received; and `&>/dev/null` means that the command substitution can't _ever_ evaluate to anything, so you're running effectively `if [ ] && [ -f .envrc ]; then`) – Charles Duffy Nov 05 '20 at 22:21
  • 2
    Note that `sh script.sh` **isn't guaranteed to support any bash-only syntax at all** (it _might_ support some, depending on which shell implements `sh` on your operating system; but even if that shell is `bash`, it turns off some features when invoked under the `sh` name). If you want to write your script to be run with bash, you need to run it *with bash*, not with `sh`. – Charles Duffy Nov 05 '20 at 22:22
  • Thanks for all these suggestions. I've attached at the end part of the `set -x` output which I think is more relevant to the case. – Barnercart Nov 05 '20 at 22:29
  • Running it with bash actually works! So that's the reason why all the exports where messed up... – Barnercart Nov 05 '20 at 22:35
  • _nod_, `direnv export bash` is not `direnv export sh`; it's allowed to use bash-only syntax, of which `$''` is an example. – Charles Duffy Nov 05 '20 at 22:41
  • BTW, this is part of why using `.sh` extensions on scripts, and _especially_ bash scripts, is so frowned on in some quarters. (You run `pip`, not `pip.py`; and `ls`, not `ls.elf` -- executable names don't conventionally have extensions on UNIX. Use an extension when you're writing a library to be sourced in, to indicate which shells it's compatible with; leave them out when you're building an executable). – Charles Duffy Nov 05 '20 at 22:43

1 Answers1

4

direnv export bash is using some bash-only syntax; specifically, ANSI C strings:

DIRENV_DIR=$'-/home/bernardo/Development/kitty'

In bash, $'...' honors escapes inside ... -- so \t can be used to refer to a tab, for example. In sh, it's not a recognized syntax feature at all.

Thus you need to run bash yourscript, not sh yourscript -- or, better, start the script with a #!/usr/bin/env bash shebang, and run it with /path/to/yourscript (or just yourscript if it's installed at a location in the PATH), letting the shebang be used to select an interpreter.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441