0

brew is installed on the server but when I sshpass to the server, it throws bash: brew: command not found and same is happening for ideviceinstaller bash: ideviceinstaller: command not found

This is the command I'm using

sshpass -p password ssh -oStrictHostKeyChecking=no -oCheckHostIP=no user@**** 'cd /Users/user/Documents/workspace/iOS; brew'
sshpass -p password ssh -oStrictHostKeyChecking=no -oCheckHostIP=no user@**** 'cd /Users/user/Documents/workspace/iOS; ideviceinstaller -l'

I have exactly same .bashrc and .bash_profile.

[[ -s "$HOME/.profile" ]] && source "$HOME/.profile" # Load the default .profile

[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm" # Load RVM into a shell session *as a function*

export ANDROID_HOME=/Users/user/Library/Android/sdk

export GIT_SSH_COMMAND='ssh -o KexAlgorithms=+diffie-hellman-group1-sha1'

export PATH=$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools:$PATH

# Setting PATH for Python 3.7
# The original version is saved in .bash_profile.pysave
PATH="/Library/Frameworks/Python.framework/Versions/3.7/bin:${PATH}"
export PATH

.profile


# Add RVM to PATH for scripting. Make sure this is the last PATH variable change.
export PATH="$PATH:$HOME/.rvm/bin"

[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm" # Load RVM into a shell session *as a function*

Not sure what mistake I'm making. Please help

Ram
  • 1

1 Answers1

1

You're getting bitten by the combination of ssh and the notoriously unintuitive way the bash shell chooses the login scripts it executes.

From the ssh(1) man page (emphasis mine):

When the user's identity has been accepted by the server, the server either executes the given command in a non-interactive session or, if no command has been specified, logs into the machine and gives the user a normal shell as an interactive session. [...]

If an interactive session is requested ssh by default will only request a pseudo-terminal (pty) for interactive sessions when the client has one. [...]

If no pseudo-terminal has been allocated, the session is transparent and can be used to reliably transfer binary data.

It does not explicitly say it, but when you specify a command to run at the remote end, ssh assumes that you are running a non-interactive session, and will not assign a pseudo-terminal (pty) at the remote end unless you use the -t option to ssh.

This turns out to be important for the shell at the remote end.

From the bash(1) man page:

A login shell is one whose first character of argument zero is a -, or one started with the --login option.

An interactive shell is one started without non-option arguments (unless -s is specified) and without the -c option whose standard input and error are both connected to terminals (as determined by isatty(3)), or one started with the -i option. [...]

You've got no (pseudo-)terminal, and do have a command to run as a set of non-option arguments, so bash is getting started as a non-interactive login shell. So what scripts does it run in that situation?

When bash is invoked as an interactive login shell, or as a non-interactive shell with the --login option, it first reads and executes commands from the file /etc/profile, if that file exists. After reading that file, it looks for ~/.bash_profile, ~/.bash_login, and ~/.profile, in that order, and reads and executes commands from the first one that exists and is readable.

So, the /etc/profile and your ~/.bash_profile may or may not be getting executed, depending on whether or not your shell gets an explicit --login option.

How about ~/.bashrc?

When an interactive shell that is not a login shell is started, bash reads and executes commands from /etc/bash.bashrc and ~/.bashrc, if these files exist.

So ~/.bashrc is only for interactive shells and will be skipped, as will be /etc/bash.bashrc.

When bash is started non-interactively, to run a shell script, for example, it looks for the variable BASH_ENV in the environment, expands its value if it appears there, and uses the expanded value as the name of a file to read and execute. Bash behaves as if the following command were executed:

         `if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi`

but the value of the PATH variable is not used to search for the filename.

So unless $BASH_ENV is set (by either sshd or the system default environment), which would be unusual, then it is possible that no login script will be executed at all in the case of a non-interactive login.

As a result, your PATH may be drastically shorter than usual when you're running non-interactive commands. You can verify this yourself this way:

local$ ssh remote
remote$ printenv PATH
/usr/local/sbin:/Users/<username>/perl5/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin
remote$ exit
local$ ssh remote printenv PATH
/usr/bin:/bin:/usr/sbin:/sbin

This example was created using macOS 10.14.6 as the remote host. Note that in particular /usr/local/bin is missing from the PATH on a non-interactive session.

As a workaround, you may have to explicitly source one or more of the normal login script(s). Example:

local$ ssh remote "source /etc/profile; printenv PATH"
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/MacGPG2/bin:/opt/X11/bin

Note that the PATH is better but still not quite the same as in the interactive login, and sourcing ~/.bashrc does not help. This turns out to be because my ~/.bashrc has this at the very beginning:

if [ -z "$PS1" ]; then
    return
fi

On non-interactive shells, "$PS1" will not be set. So this test causes the entire ~/.bashrc to be skipped on non-interactive sessions.

Anything in login scripts that generates output should be encapsulated by such tests, or else things that pipe stuff through a SSH session, such as scp, rsync or git pull may fail encountering unexpected script output instead of the application protocol data they're waiting for.

telcoM
  • 87,318
  • 3
  • 112
  • 232