0

I'm trying to write a bash script on Ubuntu 18. I have the below script ...

#!/bin/bash -l

ssh myuser@remote-machine 'mysql -u $DB_USER --password=$DB_PASS $DB_NAME < /tmp/dump.sql'

My primary problem right now is I want the environment variables to be interpreted based on the remote machine's environment, not on the environment from which I'm running my bash script. I think right now, the mysql command is using the current environment. What changes do I need to make so the environment variables are used from the remote machine's env?

Dave
  • 2,548
  • 3
    No, the variables are expanded by the remote machine in your example. What makes you think they are expanded locally? Where are those variables defined on your remote machine? Your regular startup files are probably not being read here. – terdon Jun 18 '20 at 15:56
  • 1
    To elaborate on terdon’s comment, the single ticks in your example will prevent the variables expanding in your local environment. – bxm Jun 18 '20 at 16:09
  • 1
    Typically, you need to source the remote user's profile within the remote command, by inserting between the ' and the mysql: . ~/.profile; . You can debug the remote profile by sending it back to your local machine with echo commands, or declare -p "${@}" | cut -c -200 for the full story. – Paul_Pedant Jun 18 '20 at 16:22
  • Have you tried using scp to copy the script to the remote computer and then using ssh to run the script on the remote machine? https://stackoverflow.com/questions/305035/how-to-use-ssh-to-run-a-shell-script-on-a-remote-machine – summertime Jun 18 '20 at 18:03
  • @Paul_Pedant, The answer was what you suggested -- running the profile script with the env vars prior to running the mysql command. – Dave Jun 18 '20 at 18:30
  • That's good. The issue is that shells read the .profile when they get a login. But an ssh does not seem to count as a login. The same problem (and solution) happens with crontab jobs. DataBase jobs tend to have a lot of environment additions anyway, which makes things worse. – Paul_Pedant Jun 18 '20 at 19:09

1 Answers1

0

As noted in comments to your question (terdon, bxm), the single quotes prevents your command from being expanded locally.

However, the remote environment is likely not populated because, quoting man 1 ssh (bold text mine):

If a command is specified, it is executed on the remote host instead of a login shell.

A remote login shell can be explicitly invoked, though:

ssh myuser@remote-machine bash -lc \
  "'"'mysql -u "$DB_USER" --password="$DB_PASS" "$DB_NAME" </tmp/dump.sql'"'"

Note the double layer of quoting: we make sure that the whole command is not expanded locally, by means of the inner single quotes, and that the remote host receives it enclosed in single quotes (the outer double-quoted ones), so that it doesn't undergo expansion and splitting before it is passed to Bash as the command string.

Alternatively, with no need for the extra quoting, you can have a remote login shell by serving your command to ssh through standard input and invoking ssh without a command argument:

echo 'mysql -u "$DB_USER" --password="$DB_PASS" "$DB_NAME" </tmp/dump.sql' |
  ssh -T myuser@remote-machine

-T is used to avoid the warning about a pseudo-terminal not being allocated when standard input is not a terminal.

Since your question is about Bash: a here string can also be used, adding a bit convenience:

ssh -T myuser@remote-machine <<<'mysql ...'

The actual kind of shell you need depends on how the remote environment is set up. When invoked as a non-interactive login shell, Bash sources several scripts: /etc/profile (if it exists), then the first one that is found and readable among ~/.bash_profile, ~/.bash_login, and ~/.profile; it likely won't source ~/.bashrc. You may need to adjust your actual command based on the files that set the required environment variables.

fra-san
  • 10,205
  • 2
  • 22
  • 43