The Adventures of Systems Boy!

Confessions of a Mac SysAdmin...

Using SSH to Send Variables in Scripts

In July I posted an article about sending commands remotely via ssh. This has been immensely useful, but one thing I really wanted to use it for did not work. Sending an ssh command that contained a variable, via a script for instance, would always fail for me, because, of course, the remote machine didn't know what the variable was.

Let me give an example. I have a script that creates user accounts. At the beginning of the script it asks me to supply a username, among other things, and assigns this to a variable in the script called $username. Kinda like this:

echo "Please enter the username for the new user:"
read username


Later in the script that variable gets called to set the new user's username, and a whole bunch of other parameters. Still later in the script, I need to send a command to a remote machine via ssh, and the command I'm sending contains the $username variable:

ssh root@home.account.server 'edquota -p systemsboy $username'


This command would set the quota of the new user $username on the remote machine to that of the user systemsboy. But every time I've tried to include this command in the script, it fails, which, if you think about it, makes a whole lot of sense. See, 'cause the remote machine doesn't know squat about my script, and when that command gets to the remote machine, the remote machine has no idea who in the hell $username is. The remote machine reads $username literally, and the command fails.

The solution to this is probably obvious to hard-core scripters, but it took me a bit of thinkin' to figure it out. The solution is to create a new variable that is comprised of the ssh command calling the $username variable, and then call the new variable (the entire command) in the script. Which looks a little something like this:

quota=`ssh -t root@home.account.server "edquota -p systemsboy $username"`
echo "$quota"


So we've created a variable, called $quota, which is the entire ssh command, and then we've simply called that variable in the script. That $quota variable will have the $username variable already filled in, and the command will now succeed on the remote machine. One thing that's important to note here: generally the command being sent over ssh is enclosed in single-quotes. In this instance, however, it must be enclosed in double-quotes for the command to work. I also used the -t option in this example (which tells ssh that the session is interactive, and to wait until it's told to return to the local machine) but I don't actually think it's necessary in this case. Still, it shouldn't hurt to have it there, just in case something goes funky.

But so far nothing has gone funky. This seems to work great.

Labels: , ,

« Home | Next »
| Next »
| Next »
| Next »
| Next »
| Next »
| Next »
| Next »
| Next »
| Next »

2:54 AM

I'm confused. Must not be hardcore. ;)

Are you running a script locally or on a remote machine, or are you sending a script in a ssh command?

I've sent commands before via the ssh trick, but not scripts, let alone complicated ones with variables. maybe I'm just not ready. But I want to learn. That's the first step, right? he he

Now if someone will help me figure out why my baby is crying, maybe then my brain will stop throbbing. poor 3 month old. it's only midnight and she doesn't want to sleep. diaper. check. food. check. comfort. check. daddy tired. check    



3:48 AM

MatX,

No, not sending a script. From within a script on a local machine, I'm sending a command to a remote machine via ssh. That command contains a variable that's declared in the script on the local machine. The variable is the problem.

Take an abbreviated example. This script will ask you for a username, and then try to log into a remote server and list that user's home account:
______________

#!/bin/bash

## First we declared the variable username as defined by user input

echo "Enter User Name and press return..."
read username

## Next we use ssh to send a command that contains that variable, in this case, list the home of the user in question

ssh -t root@remote.host 'ls ~/$username'

## This command goes to the remote machine with $username undefined, so this method WILL NOT work

exit
______________



Instead we need to send the command with the $username variable pre-filled. So we wrap the whole ssh command in its own variable. Here's the new script:
______________

#!/bin/bash

## First we declared the variable username as defined by user input

echo "Enter User Name and press return..."
read username

## Next we create a variable called "command", which contains the whole pre-filled ssh command

command=`ssh -t root@remote.host "ls ~/$username"`

## Then we just send the whole ssh command to the shell, i.e. execute that whole, pre-filled ssh command

echo "$command"

exit
______________

Hope this clears things up a bit. I know it's confusing, and I'm having a hard time explaining it clearly. But basically, if your remote machine doesn't understand the script variables on your local machine, any command you send via ssh that references variables from that local script will fail. So you kind of have to pre-fill those variables before sending them to the remote machine.

Ugh! It may be impossible for me to say it clearly. But you should be able to cut and paste the above as examples if it helps.

Alright. First episode of Star Trek is on for the first time in, like, 10 years, so I gotta go. Yes, I am a dork.

Cheers!

-systemsboy    



3:50 AM

Oh, and maybe try not to think about this with a crying baby. That probably will not help.

-sb    



2:40 PM

One correction to the scripts in the above comment:

Wherever they read ~/$username, they should actually say ~$username. No slash.

-sb    



1:06 AM

Thanks for clarifying it for me. Now it makes sense. I played around with your example scripts, and now I get what you were trying to say. When I found out after years of using ssh, that you could actually send a command iwht it, I was excited, but now you're saying I can use variables too. I just don't know what to say. This is cool. Awesome.

------

I modfied the first script while I figuring out why it wouldn't work:

----

#!/bin/bash

## First we declared the variable username as defined by user input

echo "Enter User Name and press return..."
read username

## Next we use ssh to send a command that contains that variable, in this case, list the home of the user in question

ssh -p XXXX $username@XX.XX.XX.XX 'ls'

## This command goes to the remote machine with $username undefined, so this method WILL NOT work

exit

------

Then I modified the second one to ask for username, then command:

----

#!/bin/bash

## First we declared the variable username as defined by user input

echo "Enter User Name and press return..."
read username

##define new variable

echo "Enter command and press return..."
read command

## Next we create a variable called "command", which contains the whole pre-filled ssh command

bigbang=`ssh -p XXXX $username@XX.XX.XX.XX "$command"`

## Then we just send the whole ssh command to the shell, i.e. execute that whole, pre-filled ssh command

echo "$bigbang"

exit
------

Fun.    



2:01 AM

That's it! You got it!

Yes, it is quite fun.

-systemsboy    



1:31 AM

Hi SB and matx...

I clearly understand what you said and its nice...

But I have a different problem... is it possible to get the remote systems HOME directory from ssh..?

ssh -t root@remote.host 'echo $HOME'

I tried this but i get only the local systems $HOME...

however.. when i try

ssh -t root@remote.host 'pwd'

I get the correct results...

any suggestions??    



2:10 AM

Uh, that's weird. It works fine for me. I do:
ssh -t user@rhost 'echo $HOME'

And I get the home directory of the the specified user (user) on the remote machine (rhost).

You might also try this form:
ssh -t user@rhost 'printenv HOME'

Make sure you're using single-quotes around the command, not double-quotes which will cause strange behavior.

The next thing I'd do is check and make sure the $HOME variable is set properly on the remote machine by logging in and checking it directly. So, ssh to your remote machine, and run echo $HOME (or printenv HOME). If it doesn't return the expected value, something's just screwy with environment variables on the remote machine. You might also run printenv on the remote machine, and check all the values to make sure everything's proper. If not, you need to fix something.

The only other thing I can think of is that maybe the default shell on the remote machine is different and doesn't understand echo $HOME. I don't have enough experience with shells other than bash and tcsh to know if that's a possible issue or not, though my tests of sh and csh both give me the desired results, so I doubt that's the problem.

Actually, that's all I can think of offhand.

Let me know if you find the answer.

-systemsboy    



11:58 AM

CHEERS!!

Yes the culprit was the double quote... if try double quout with pwd, ls... it works fine ... but with $HOME i need to use single quoute.

Thanks SB.    



12:01 PM

No problem. Glad to help.

-systemsboy    



12:08 PM

I have one more question.. I read somewhere that we can also give a script as input to the ssh so that script will be executed on the remote host...

ssh -t root@remote.host script_to_run_on_remote_host.sh

did anyone try this..?    



12:38 PM

I would assume the script would have to live on and be runnable from the remote host for it to run. Remember, once you ssh -t to a remote host, commands are run from the remote machine, so if the script doesn't exist on that machine, or if the command paths differ, it won't work. If the script is there and can properly run, however, it should run just fine from ssh -t.

If the script only lives on the local host, you might be able to pipe its commands somehow into your script that uses ssh -t, but it's going to be a real pain. I don't know an easy way to do this. You might try something like:

script=`cat path_to_script.sh`
ssh -t user@rhost 'script'

This MIGHT work, but I honestly doubt it, because the remote host has to go through that script line by line, which it can't actually do because it doesn't have a copy of the script.

So, I don't know the answer to this question for sure — it's possible there's some more hidden magic to ssh that I just don't know of — but I'm going to say that I doubt this is something you can do very easily, if at all. It will be easier to copy the script over to the remote host, verify that it works properly, and then run it using ssh -t.

You can, however, run a string of commands, separated by semi-colons, with ssh -t, like so:

ssh -t user@rhost 'ls -la; who; df -h'

-systemsboy    



9:30 PM

Thanks Dude!!

I have got my purpose solved.

I get the script ready in server A. Then SCP it over to server B and then ssh and run that script. :-)

copy dynamically generated script from local server to remote host:

scp sshremote.sh user@rhost:'$HOME'

Run that script on remote server from local server:
ssh -t user@rhost '$HOME/sshremote.sh'

:-)    



10:30 PM

Excellent! Congratulations!

-systemsboy    



4:45 PM

Do you know if in the string of commands send by ssh is it possible to include an if statement?
ssh user@host 'if [ ]; then command;fi'    



8:30 PM

That's a great question. I don't know the answer offhand, but it should be easy to simply test it and see if it works. I'd imagine that it does. Sending commands via SSH seems to work pretty much like running them locally. Give it a whirl with something like:

ssh -t user@host 'if [ `ls -a | grep .Trash` == .Trash ]; then echo "Hello"; fi'

Actually, I just tried this. So, to answer your question: Yes. It works perfectly.

Now you know. And knowing is half the battle.

-systemsboy    



8:06 PM

is it possible for me to change a users password with this method?

-TC    



11:29 AM

Should be. Why don't you give it a try?

ssh -t user@host 'passwd username'

-systemsboy    



4:48 PM

Hello SB,

I am having some serious dificulties regarding a ssh runing remote script a sent by a local script in the midle of a loop:

#!/bin/bash
while [statement]; do
yada; yada; yada;
if [statement]; then
ssh -p xxxx $USER@$HOST `remote_script.sh`
fi
yada; yada; yada;
done

The thing is, even if the while has many things else to do, the first time it runs the remote script (or command) via ssh, the while exits with no particular reazon.

It does not matter if the remote commands ends up successfully or not.

Is there any mandatory "DROP ALL LOOPS" after any SSH sort of undocumented "feature"?

The real bizzar portion is, that if I force the remote script to run on the backgrounf with a & like this:

#!/bin/bash
while [statement]; do
yada; yada; yada;
if [statement]; then
ssh -p xxxx $USER@$HOST `remote_script.sh &`
fi
yada; yada; yada;
done

...it goes on, but the remote_script.sh is supposed to run serialized and never in paralel with another instance of it self, therefore crasing.

Thanx in advance.    



5:33 PM

Me and a friend had the same problem, and after a while strugling with the script and debuging.

It seemes the ssh messes with the stdin and halted our while statement.

Try setting -n on the ssh options. It worked for us.    



6:31 PM

right so you went the round about way of doing this. the problem was with variable expansion.

Here's a simple two liner for yah:
echo -n "Username: " && read username
ssh -t root@remote.site "ls ~${username}"

what happens here is when you put a variable name within ${} instead of just a $var_name the shell expands it, like hitting tab on the cli or echoing the values. This is not only useful in this case but also if you have ANSI colors, or values returned from a command.    



8:05 PM

Awesome! I haven't tried this, but this sounds like exactly what I was looking for a way to do and not finding. Hence my roundabout method. If this works for me I'll update my post.

Thanks!

-systemsboy    



4:13 PM

how does this work if you need to login to multiple servers and run the command at the same time?    



9:41 AM

THANKS THANKS THANKS!!!!

From Chile    



7:45 PM

pradeep,

You can either log in to each machine 1-at-a-time, or you can use a for or while loop to automate the process, as per the comments in this article:
http://systemsboy.blogspot.com/2006/07/send-remote-commands-via-ssh.html

-systemsboy    



1:54 PM

nice workaround, saved me a lot of brain hours...    



» Post a Comment