Having trouble with running a command over ssh - linux

This command isn't working as expected:
ssh root#<machineIP> -- sh -c "echo \$\(cat /tmp/testfile\) > /testfile"
My intent is to copy the contents of /tmp/testfile to /testfile. Real simple. But I find that /testfile has nothing in it. The file is created (in the case it doesn't exist).
echo command works fine (minus the escapes) if run from command line on the remote server. But doesn't work when running it through ssh. Originally I actually had a more complex command with 'sed', but simplified what wasn't working down to this.
Both remote and local servers are CentOS7.
I found I had to escape the $ and (). Is this causing me problems? Is not the correct way to run this command?

ssh will take all the command parameters, join them on spaces and then run that in the remote shell.
That means that the command being executed is basically what you'd get if you replaced ssh with echo:
$ echo sh -c "echo \$(cat /tmp/testfile) > /testfile"
sh -c echo $(cat /tmp/testfile) > /testfile
The quoting is lost, so the resulting command is equivalent to sh -c echo > /testfile which makes it empty.
Instead, just take the command you want to run and wrap it in single quotes:
ssh root#host 'echo $(cat /tmp/testfile) > /testfile'
Make sure never to use echo or $() when copying files though. I'm assuming this is a stand-in for something better.

Related

shell script can't see files in remote directory

I'm trying to write an interactive script on a remote server, whose default shell is zsh. I've been trying two different approaches to get this to work:
Approach 1: ssh -t <user>#<host> "$(<serverStatusReport.sh)"
Approach 2: ssh <user>#<host> "bash -s" < serverStatusReport.sh
I've been using approach 1 just fine up until now, when I ran into the following issue - I have a block of code that runs depending on whether certain files exist in the current directory:
filename="./service_log.*"
if ls $filename 1> /dev/null 2>&1 ; then
echo "$filename found."
##process files
else
echo "$filename not found."
fi
If I ssh into the server and run the command directly, I see "$filename found."
If I run the block of code above using Approach 1, I see "$filename not found".
If I copy this block into a new script (lets call this script2), and run it using Approach 2, then I see "$filename found".
I can't for the life of me figure out where this discrepancy is coming from. I thought that the difference may be that script2 is piped into bash whereas my original script is being run with zsh... but considering that running the same command verbatim on the server, with its default zsh shell, returns correctly... I'm stumped.
:( any help would be greatly appreciated!
I guess that when executing your approach 1 it is the local shell that expands "$(<serverStatusReport.sh)", not the remote. You can easily check this with:
ssh -t <user>#<host> "$(<hostname)"
Is the serverStatusReport.sh script also in the PATH on the local host?
What I do not understand is why you get this message instead of an error message.

coloring terminal with shell script

Someone can explain me why when I copy and paste the following command in the terminal it displays the colorful test correctly, but when I run it via sh myscript.sh it does not display the colored text?
blue='\e[1;34m'
NC='\e[0m'
echo -e "${blue}Test${NC}"
EDIT
Sudo is not the problem. If I copy the above and paste directly into the terminal, everything works. If you run through file, sh myscript.sh not work
Probably because sh isn't bash on your system.
$ file /bin/sh
/bin/sh: symbolic link to `dash'
Try
bash myscript.sh
Your interactive shell seems to be GNU Bash, while sh is a generic POSIX shell, which actually may be dash, busybox sh or something else. The problem is that neither -e option for echo nor \e are POSIX-compliant.
But you can easily use printf instead of echo -e (do not forget to explicitly specify newline character \n) and \033 instead of \e:
blue='\033[1;34m'
NC='\033[0m'
printf "${blue}%s${NC}\n" 'Test'
Or, of course, you can just use bash (as Elliott Frisch suggested) if you are sure that it would be available on target system.
Also I should point out, that what you done is not right way to run shell scripts at all. If you’re writing a standalone script, then you’d better to use hashbang and set execution bit to file.
$ cat myscript
#!/bin/sh
blue='\033[1;34m'
NC='\033[0m'
printf "${blue}%s${NC}\n" 'Test'
$ chmod +x myscript
$ ./myscript
But if you’re writing a command sequence (a macros, if you will) for interactive shell, there is source (or simply .) command:
$ source myscript
(Then all of above about POSIX-compliance does not matter of course.)

Passing arguments to a shell script to be executed within ssh

I am trying to write a deployment script that will take the command I need to run on the remote machine and then run it on the remote machine. What I am envisioning is something as follows:
./DeployAndRunScript.sh "java -jar runmyjar.jar -t args &"
In my DeployAndRunScript.sh, the very last line is:
ssh -i $pemfile $targetmachine "java -jar $runcommandhere"
I was wondering, what would be a way to get the arguments from the commandline into the "runcommandline" alias and execute it within the script?
To get the arguments in a shell script you can type $#. Taking that a step further, you can simply change the last line to
ssh -i $pemfile $targetmachine "$#"
(edit: added quotes around $#, nice catch Barmar!)
You could replace $# with $1 if you know that the command will be the first argument wrapped in quotes.

How to pass local variable to remote ssh commands?

I need to execute multiple commands on remote machine, and use ssh to do so,
ssh root#remote_server 'cd /root/dir; ./run.sh'
In the script, I want to pass a local variable $argument when executing run.sh, like
ssh root#remote_server 'cd /root/dir; ./run.sh $argument'
It does not work, since in single quote $argument is not interpreted the expected way.
Edit: I know double quote may be used, but is there any side effects on that?
You can safely use double quotes here.
ssh root#remote_server "cd /root/dir; ./run.sh $argument"
This will expand the $argument variable. There is nothing else present that poses any risk.
If you have a case where you do need to expand some variables, but not others, you can escape them with backslashes.
$ argument='-V'
$ echo "the variable \$argument is $argument"
would display
the variable $argument is -V
You can always test with double quotes to discover any hidden problems that might catch you by surprise. You can always safely test with echo.
Additionally, another way to run multiple commands is to redirect stdin to ssh. This is especially useful in scripts, or when you have more than 2 or 3 commands (esp. any control statements or loops)
$ ssh user#remoteserver << EOF
> # commands go here
> pwd
> # as many as you want
> # finish with EOF
> EOF
output, if any, of commands will display
$ # returned to your current shell prompt
If you do this on the command line, you'll get a stdin prompt to write your commands. On the command line, the SSH connection won't even be attempted until you indicate completion with EOF. So you won't see results as you go, but you can Ctrl-C to get out and start over. Whether on the command line or in a script, you wrap up the sequence of commands with EOF. You'll be returned to your normal shell at that point.
You could run xargs on the remote side:
$ echo "$argument" | ssh root#remote_server 'cd /root/dir; xargs -0 ./run.sh'
This avoids any quoting issues entirely--unless your argument has null characters in it, I suppose.

Avoid gnome-terminal close after script execution?

I created a bash script that opens several gnome-terminals, connect to classroom computers via ssh and run a script.
How can I avoid that the gnome-terminal closes after the script is finished? Note that I also want to be able to enter further commands in the terminal.
Here is an example of my code:
gnome-terminal -e "ssh root#<ip> cd /tmp && ls"
As I understand you want gnome-terminal to open, have it execute some commands, and then drop to the prompt so you can enter some more commands. Gnome-terminal is not designed for this use case, but there are workarounds:
Let gnome-terminal run bash and tell bash to run your commands and then start a new bash
$ gnome-terminal -- bash -c "echo foo; echo bar; exec bash"
or if the commands are in a script
$ gnome-terminal -- bash -c "./scripttorun; exec bash"
The first bash will terminate once all the commands are done. But the last command is a new bash which will then just keep running. And since something is still running gnome-terminal will not close.
Let gnome-terminal run bash with a prepared rcfile which runs your commands
Prepare somercfile:
source ~/.bashrc
echo foo
echo bar
Then run:
$ gnome-terminal -- bash --rcfile somercfile
bash will stay open after running somercfile.
i must admit i do not understand completely why --rcfile has this behaviour but it does.
Let gnome-terminal run a script which runs your commands and then drops to bash
Prepare scripttobash:
#!/bin/sh
echo foo
echo bar
exec bash
Set this file as executable.
Then run:
$ gnome-terminal -- ./scripttobash
for completeness
if you just want to be able read the output of the command and need no interactivity
go to preferences (hamburger button -> preferences)
go to profiles (standard or create a new one)
go to command tab
when command exits -> hold the terminal open
i recommend to create a new profile for just for this use case.
use the profile like this:
gnome-terminal --profile=holdopen -- ./scripttorun
Every method has it's quirks. You must choose, but choose wisely.
I like the first solution. it does not need extra files or profiles. and the command says what it does: run commands then run bash again.
All that said, since you used ssh in your example, you might want to take a look at pssh (parallel ssh). here an article: https://www.cyberciti.biz/cloud-computing/how-to-use-pssh-parallel-ssh-program-on-linux-unix/
Finally this one works for me:
gnome-terminal --working-directory=WORK_DIR -x bash -c "COMMAND; bash"
Stack Overflow answer: the terminal closes when the command run inside it has finished, so you need to write a command that doesn't terminate immediately. For example, to leave the terminal window open until you press Enter in it:
gnome-terminal -e "ssh host 'cd /tmp && ls'; read line"
Super User answer: Create a profile in which the preference “Title and Command/When command exits” is set to “Hold the terminal open”. Invoke gnome-terminal with the --window-with-profile or --tab-with-profile option to specify the terminal name.
Run with -ic instead -i to make terminal close bash proccess when you close your terminal gui:
gnome-terminal -e "bash -ic \"echo foo; echo bar; exec bash\""
As of January 2020, the -e option in gnome-terminal still runs properly but throws out the following warning:
For -e:
# Option “-e” is deprecated and might be removed in a later version
of gnome-terminal.
# Use “-- ” to terminate the options and put the command line to
execute after it.
Based on that information above, I confirmed that you can run the following two commands without receiving any warning messages:
$ gnome-terminal -- "./scripttobash"
$ gnome-terminal -- "./genericscripttobash \"echo foo\" \"echo bar\""
I hope this helps anyone else presently having this issue :)
The ideal solution would be to ask for a user input with echo "Press any key".
But if double-click in Nautis or Nemo and select run in a terminal, it doesn't seem to work.
In case of Ubuntu a shell designed for fast start-up and execution with only standard features is used, named dash I believe.
Because of this the shebang is the very first line to start with to enable proper use of bash features.
Normally this would be: #!/bin/bash or similar.
In Ubuntu I learned this should be: #!/usr/bin/env bash.
Many workarounds exist to keep hold of the screen before the interpreter sees a syntax error in a bash command.
The solution in Ubuntu that worked for me:
#!/usr/bin/env bash
your code
echo Press a key...
read -n1
For a solution applicable to any terminal, there is a script that opens a terminal, runs the command specified and gives you back the prompt in that new terminal:
https://stackoverflow.com/a/60732147/1272994
I really like the bash --rcfile method
I just source ~/.bashrc then add the commands I want to the new startrc.sh
now my automated start.sh work environment is complete... for now 😼
If running a bash script just add gedit afile to the end of the script and that will hold gnome-terminal open. "afile" could be a build log which it was in my case.
Did not try just using gedit alone but, that would properly work too.
Use nohup command.
nohup gnome-terminal -e "ssh root# cd /tmp && ls"
Hope this will help you.

Resources