Bash, Fish: prevent variable expansion

98 views Asked by At

Am planing to switch to fish as my default shell but am having some troubles with it.
Before rewrite all my script I would like to reuse the old ones using bash with '-c' option.
Example:

bash -c "cat <<'EOF' | tee filename >/dev/null
$SHELL
$TERM
EOF"

Even using 'EOF', I always get:
/bin/fish
xterm

If posible, I would like to avoid to use a backslash \$SHELL in every variable in the script.

1

There are 1 answers

1
faho On

The main issue here is that $SHELL does not mean what you think it means.

$SHELL is not "the currently running shell", it is "the user's configured login shell".

Neither bash nor fish (nor zsh for that matter) will set it, they'll inherit it from your login stack.

That means either what you are actually trying to do will not work, or your test will have misleading results.

The next step is that the argument, to fish, is double-quoted:

bash -c "..."

That means variables inside those will be expanded by fish, before bash runs. You can single-quote them:

bash -c '...'

This means you'll need to escape any single-quotes in the argument like

bash -c 'cat <<\'EOF\' | tee filename >/dev/null                                               
$SHELL
$TERM
EOF'

Of course this will tell bash to do

cat <<'EOF' | tee filename >/dev/null
$SHELL
$TERM
EOF

which means bash won't expand the variables either. If that's what you want, to print a literal "$SHELL" and "TERM", that's fine. If not, you can use EOF without the quotes to let bash expand the variables.

This won't look any different because (see above) $SHELL won't change between the two shells. You can use a variable only set in bash to confirm it's working:

bash -c 'cat <<EOF | tee filename >/dev/null                                               
$BASH_VERSION
$TERM
EOF'

this will give you your bash's version in "filename".

The issues with nested quoting and escaping and which shell expands which variable are why I would usually recommend just writing a bash script.

Make a file called "printshell" with these contents:

#!/bin/bash

cat <<EOF | tee filename >/dev/null
$SHELL
$TERM
EOF

make it executable (chmod +x) and put it somewhere in your $PATH. If you don't have a suitable directory you can create one (mkdir) and add it to fish's $PATH with fish_add_path. This allows you to run it as printshell without having to use the full path, like it's a function in the shell.

(of course it won't have access to the shell's state, so you won't have all your variables, only the exported ones, or be able to cd and have that stick outside. For that you'll have to use fish script)