4

I want to SSH into a server, start a screen session, cd into path/to/my/script/, and run test.sh there.

As a starter, I tried

ssh [email protected] screen -dm bash -c 'cd  path/to/my/script/; pwd > ~/output.txt'

and expected to see path/to/my/script/ in output.txt, but I see my home directory there. This means the cd command doesn't really work, so bash won't be able to run test.sh.

How may I solve this?

3
  • 2
    In my test, it does work if I add extra double quotes for command executed by ssh: ssh [email protected] "screen -dm bash -c 'cd path/to/my/script/; pwd > ~/output.txt'"
    – brm
    Commented Jan 2, 2017 at 7:59
  • @brm Probably your comment should be turned into an answer.
    – migrc
    Commented Jan 2, 2017 at 9:55
  • 1
    @migrc Hope you like it :)
    – brm
    Commented Jan 2, 2017 at 12:50

2 Answers 2

5

The short answer: add some extra quotes around the command, like this:

ssh [email protected] "screen -dm bash -c 'cd path/to/my/script/; pwd > ~/output.txt'" 

To see what's going on, you can specify the -v option to ssh to obtain some debug information. In this case, you'll see a line like the following for the original command

debug1: Sending command: screen -dm bash -c cd path/to/my/script/; pwd > ~/output.txt

while the extra quotes change this into

debug1: Sending command: screen -dm bash -c 'cd path/to/my/script/; pwd > ~/output.txt'

So it appears that ssh just takes the arguments that were passed to it, concatenates them all, and lets the remote side split the concatenated argument list again into individual arguments. Calling the argument list argv (like in C), you've got something like the following in the original version:

argv[0] = ssh
argv[1] = [email protected]
argv[2] = screen
argv[3] = -dm
argv[4] = bash
argv[5] = -c
argv[6] = cd path/to/my/script/; pwd > ~/output.txt

Now in principle, it would have been possible for ssh to pass argv[2] to argv[6] as separate arguments to the other side, in which case it would probably have worked as expected. But as the debug line shows (and it also seems like this based on the source code), these arguments are concatenated to the string

screen -dm bash -c cd path/to/my/script/; pwd > ~/output.txt

which is then interpreted at the remote end. From this it's also clear why it doesn't do what you'd like: now you're executing two things in sequence, first screen -dm bash -c cd path/to/my/script/ (so a screen session is started in which only the directory is changed) is executed from the home directory, and then pwd > ~/output.txt is executed, also from the home directory.

For completeness, the arguments for the command with the double quotes are

argv[0] = ssh
argv[1] = [email protected]
argv[2] = screen -dm bash -c 'cd path/to/my/script/; pwd > ~/output.txt'

causing screen -dm bash -c 'cd path/to/my/script/; pwd > ~/output.txt' to be sent to the other side (as shown by the debug line), which does work as intended.

3
  • Really good answer!
    – migrc
    Commented Jan 2, 2017 at 13:28
  • This works, thanks! But what if I need to expand variables within the single quotes? Like cmd=pwd; ssh [email protected] "screen -dm bash -c 'cd path/to/my/script/; ${cmd} > ~/output.txt'". Like that, cmd doesn't get expanded... Commented Jan 2, 2017 at 16:34
  • Yes, within single quotes shell variables don't get substituted. You can use escaped double quotes however: ssh [email protected] "screen -dm bash -c \"cd path/to/my/script/; ${cmd} > ~/output.txt\""
    – brm
    Commented Jan 2, 2017 at 17:24
2

The comment from @brm is the right answer. Just add the extra double quotes and it will work:

ssh [email protected] "screen -dm bash -c 'cd path/to/my/script/; pwd > ~/output.txt'"

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .