1

I'm experiencing a strange behavior when I try to execute one of my scripts on a remote machine via ssh.

I prepend the path to my scripts dir to $PATH in my .profile. But when I run ssh kalle@Raspi foo, I get "command not found".

ssh kalle@Raspi 'echo $PATH' gives the default value, and with variables I set and export in .profile, the output is empty.

I put an echo foo in .profile, and of course, it is printed. Then I put set -vx in the first line and the order of the printed lines is weird; e. g. "foo" is printed before "+echo foo".

Can anyone figure out what's going on?

EDIT:

When I do ssh kalle@Raspi 'bash -lc "echo \$PATH"', I get my full path.

EDIT 2:

I'm sorry for the confusion, I was very tired when I wanted to run my remote script and now my memory was wrong, but I remember now what I did.

I tried with bash -lc after it didn't work with the mere command, but I missed the backslash! So I had ssh kalle@Raspi 'bash -lc "echo $PATH"' and as always when you give a command string to another command, the shell performs its various expansions on that command string.

So after quote removal, we have bash -lc "echo $PATH" which is passed to ssh as a single argument and executed on the remote machine, where the shell performs parameter substitution, i. e. $PATH is replaced by the default path before it is passed to bash -lc, making the latter useless.

And then I was stupid enough to forget that I was using bash -lc, so I thought ssh kalle@Raspi foo would cause .profile to be read because I saw the output of my echo command I had put in my .profile.

What a shame. I must have been really tired because when I wrote this question, it was absolutely natural to me that I have to protect the variable with a backslash; I didn't even have to think about it. But I still didn't realise what I realised now.

So the behavior is pretty clear and absolutely normal, but the problem remains.

I can use bash -lc as a workaround which – depending on the situation – can be a hassle with quoting and escaping, or I can put . .profile before the actual command.

Does anybody know a solution that is more elegant? Something I can adjust on the server so I can just use ssh kalle@Raspi foo?

Christoph
  • 129
  • 6
  • The issue is that the remote shell started by SSH is a non-login shell, which doesn't read your .profile file. You can force Bash to start a login shell by passing the `-l` option to Bash, like this: `ssh kalle@Raspi 'bash -lc "echo \$PATH"'`. Alternatively, you can move your path modifications to the .bashrc file and make sure that your .bash_profile file sources your .bashrc file. – kuspia Apr 04 '23 at 07:13
  • @kuspia: 1) But my .profile IS executed as my "echo" test has proven. However, it seems it isn't sourced. 2) My fried, I know that. You don't need to copy things from my question; I know what I've written ;) 3) I don't have a .bash_profile since it has no benefit over .profile if Bash is the only shell you use, as in my case. Read the section "INVOCATION" of the Bash manual. – Christoph Apr 04 '23 at 15:06
  • @kuspia: I'm sorry, you're right. ```.profile``` is NOT executed. I just tested it. – Christoph Apr 04 '23 at 15:43
  • 1
    @terdon, `.bashrc` is read by `ssh user@host 'foo bar'` ([Bash startup files / Invoked by remote shell daemon](https://www.gnu.org/software/bash/manual/html_node/Bash-Startup-Files.html)) – ilkkachu Apr 04 '23 at 17:22
  • @ilkkachu dammit, [I keep forgetting that](https://unix.stackexchange.com/q/257571/22222), thanks! But in many (most?) Linux systems, it will exit immediately because of the things explained in that question. So yes, you're right, it _is_ read, but because of the `[ -z "$PS1" ] && return` or equivalent, anything that isn't at the very beginning of the file will not be read. This is why it's confusing if you add say `echo foo` to your `~/.bashrc` and then `ssh localhost hostname`, you just get the host name and no `foo`. – terdon Apr 04 '23 at 17:35
  • Sorry, @kuspia, see above. – terdon Apr 04 '23 at 17:44
  • @terdon, oh, rats, good point! I've probably missed _that_ before, too, since I've dropped that test from my `.bashrc`... (Or rather, started from a clean file and never bothered to add it. And yes, I've learned to protect any output from there with `if tty -s` or so.) Anyway, the fact that Bash does read it in that case is somewhat odd, IMO, it doesn't seem to fit with logic it otherwise has there. – ilkkachu Apr 04 '23 at 19:35

1 Answers1

0

Thanks to @ilkkachu, I found a solution.

I moved the variables stuff from .profile to a separate script which I source from within .profile.

Then, I modified the "If not running interactively, don't do anything" part in .bashrc as follows:

case $- in
    *i*) ;;
      *)
        # Not running interactively.
        # Check if we are the server of an SSH connection without a PTY.
        # If so, sshd is about to execute a command issued by some client,
        # in which case this script has been invoked directly by sshd, not
        # by .profile, so we need to set vars that are normally set in .profile
        # and that are important for my scripts (like $PATH) here before returning.
        # This way, I can call my scripts just via ‘ssh kalle@Raspi foo’ without
        # using ‘bash -lc’.
        #
        # References:
        # https://www.gnu.org/software/bash/manual/html_node/Bash-Startup-Files.html
        # https://man.openbsd.org/ssh#ENVIRONMENT

        [[ $SSH_CONNECTION && -z $SSH_TTY ]] && . $HOME/scripts/bashrc-include/set-vars.sh
        return;;
esac

EDIT:

Not quite unimportant: Prove that it works without side effects.

I placed echoes at the head of .profile, .bashrc and set-vars.sh, and another echo after the case ... in ... esac above.

I should mention that I'm using the same .profile and .bashrc on all my machines.

So, let's test it:

When I open a terminal on the machine I'm writing this, I get:

.profile: Started.
set-vars.sh: Setting vars.
.bashrc: Started.
.bashrc: Interactive shell, proceeding.

When I ssh into my Raspi, I get exactly the same.

When I issue ssh kalle@Raspi 'echo $PATH', I get:

.bashrc: Started.
set-vars.sh: Setting vars.

and the correct $PATH.

EDIT 2:

A small script to switch the messages on and off:

#!/bin/bash

# Author: derkallevombau
# Created 2023-04-07 12:58:48

if [[ $1 =~ ^on|1|true$ ]]; then
    sedScript='"s/^#([\t ]*echo $(basename $f):.*)/\1/"'
elif [[ $1 =~ ^off|0|false$ ]]; then
    sedScript='"s/^([\t ]*echo $(basename $f):.*)/#\1/"'
else
    cat <<-END
        Unknown argument: '$1'.

        Invocation: bash-startup-messages-set (on|1|true)|(off|0|false)
    END

    exit
fi

for f in ~/.{profile,bashrc} /home/kalle/scripts/bashrc-include/set-vars.sh; do
    eval sed -Ei $sedScript $f
done
Christoph
  • 129
  • 6