Bash script runs one command before previous. I want them one after the other - linux

So part of my script is as follows:
ssh user#$remoteServer "
cd ~/a/b/c/;
echo -e 'blah blah'
sleep 1 # Added this just to make sure it waits.
foo=`grep something xyz.log |sed 's/something//g' |sed 's/something-else//g'`
echo $foo > ~/xyz.list
exit "
In my output I see:
grep: xyz.log: No such file or directory
blah blah
Whereas when I ssh to the server, xyz.log does exist within ~/a/b/c/
Why is the grep statement getting executed before the echo statement?
Can someone please help?

The problem here is that your command in backticks is being run locally, not on the remote end of the SSH connection. Thus, it runs before you've even connected to the remote system at all! (This is true for all expansions that run in double-quotes, so the $foo in echo $foo as well).
Use a quoted heredoc to protect your code against local evaluation:
ssh user#$remoteServer bash -s <<'EOF'
cd ~/a/b/c/;
echo -e 'blah blah'
sleep 1 # Added this just to make sure it waits.
foo=`grep something xyz.log |sed 's/something//g' |sed 's/something-else//g'`
echo $foo > ~/xyz.list
exit
EOF
If you want to pass through a variable from the local side, the easy way is with positional parameters:
printf -v varsStr '%q ' "$varOne" "$varTwo"
ssh "user#$remoteServer" "bash -s $varsStr" <<'EOF'
varOne=$1; varTwo=$2 # set as remote variables
echo "Remote value of varOne is $varOne"
echo "Remote value of varTwo is $varTwo"
EOF

[command server] ------> [remote server]
The better way is to create shell script in the "remote server" , and run the command in the "command server" such as :
ssh ${remoteserver} "/bin/bash /foo/foo.sh"
It will solve many problem , the aim is to make things simple but not complex .

Related

Can't run bash file inside ZSH

I've placed a bash file inside .zshrc and tried all different ways to run it every time I open a new terminal window or source .zshrc but no luck.
FYI: it was working fine on .bashrc
here is .zshrc script:
#Check if ampps is running
bash ~/ampps_runner.sh & disown
Different approach:
#Check if ampps is running
sh ~/ampps_runner.sh & disown
Another approach:
#Check if ampps is running
% ~/ampps_runner.sh & disown
All the above approaches didn't work (meaning it supposes to run an app named ampps but it doesn't in zsh.
Note: It was working fine before switching to zsh from bash. so it does not have permission or syntax problems.
Update: content of ampps_runner.sh
#! /usr/bin/env
echo "########################"
echo "Checking for ampps server to be running:"
check=$(pgrep -f "/usr/local/ampps" )
#[ -z "$check" ] && echo "Empty: Yes" || echo "Empty: No"
if [ -z "$check" ]; then
echo "It's not running!"
cd /usr/local/ampps
echo password | sudo -S ./Ampps
else
echo "It's running ..."
fi
(1) I believe ~/.ampps_runner.sh is a bash script, so, its first line should be
#!/bin/bash
or
#!/usr/bin/bash
not
#! /usr/bin/env
(2) Then, the call in zsh script (~/.zshrc) should be:
~/ampps_runner.sh
(3) Note: ~/.ampps_runner.sh should be executable. Change it to executable:
$ chmod +x ~/ampps_runner.sh
The easiest way to run bash temporarily from a zsh terminal is to
exec bash
or just
bash
Then you can run commands you previously could only run in bash. An example
help exec
To exit
exit
Now you are back in your original shell
If you want to know your default shell
echo $SHELL
or
set | grep SHELL=
If you want to reliably know your current shell
ps -p $$
Or if you want just the shell name you might use
ps -p $$ | awk "NR==2" | awk '{ print $4 }' | tr -d '-'
And you might just put that last one in a function for later, just know that it is only available if it was sourced in a current shell.
whichShell(){
local defaultShell=$(echo $SHELL | tr -d '/bin/')
echo "Default: $defaultShell"
local currentShell=$(ps -p $$ | awk "NR==2" | awk '{ print $4 }' | tr -d '-')
echo "Current: $currentShell"
}
Call the method to see your results
whichShell

remote ssh command: first echo output is lost

I'm trying to run several commands on a remote box via ssh 1-liner call by specifying them as semicolon-separated string passed to "bash -c". It works for some cases, but does not for others. Check this out:
# Note: the "echo 1" output is lost:
bash-3.2$ ssh sandbox bash -c "echo 1; echo 2; echo 3"
2
3
# Note: first echo is ignored again
bash-3.2$ ssh sandbox bash -c "echo 0; echo 1; echo 2; echo 3"
1
2
3
# But when we run other commands (for example "date") then nothing is lost
bash-3.2$ ssh sandbox bash -c "date; date;"
Wed Nov 7 20:27:55 UTC 3018
Wed Nov 7 20:27:55 UTC 3018
What am I missing?
Remote OS: Ubuntu 16.04.5 LTS
Remote ssh: OpenSSH_7.2p2 Ubuntu-4ubuntu2.4, OpenSSL 1.0.2g 1 Mar 2016
Local OS: macOS High Sierra Versoin 10.13.3
Local ssh: OpenSSH_7.6p1, LibreSSL 2.6.2
Update:
The above example is heavily simplified picture of what I'm trying to do.
The practical application is actually to generate few files on remote box by echo'ing into remote filesystem:
#!/bin/bash
A=a
B=b
C=c
ssh -i ~/.ssh/${REMOTE_FQDN}.pem ${REMOTE_FQDN} sudo bash -c \
"echo $A > /tmp/_a; echo $B > /tmp/_b; echo $C > /tmp/_c;"
After I run the above script and go to remote box to check results I see the following:
root#sandbox:/tmp# for i in `find ./ -name '_*'|sort`; do echo "----- ${i} ----"; cat $i; done
----- ./_a ----
----- ./_b ----
b
----- ./_c ----
c
As you can see the 1st "echo" command generated blank file!
To be clear, there's 3 shells at work here - the one that interprets ssh, your local shell that is; the one that ssh will be automatically running for you, and the bash you're invoking explicitly.
The reason the 1 is "disappearing" is that the shell that interprets the ssh command "eats" the quotes around the -c arguments, and then the shell on the other side of ssh splits the arguments at whitespace. So it ends up looking like bash -c echo 1 ; echo 2; echo 3. In turn, -c just gets echo, which echos an empty line; 1 becomes the value of that shell's $1, which isn't used. Then the inner bash returns, and the direct ssh shell runs the echo 2; echo 3 normally.
Consider this:
$ ssh xxx bash -c "'echo 1'; echo 2; echo 3"
1
2
3
where echo 1 is protected within the ssh arguments, so the 2nd level ssh shell is passed bash -c 'echo 1'; echo 2; echo 3. The innermost 3rd level shell echos 1, and then the 2nd level ssh shell echos 2 and 3.
Here is yet another interesting permutation:
$ ssh xxx bash -c "'echo 1; echo 2; echo 3'"
1
2
3
here, the inner shell gets all the echos as they're kept grouped within the first shell by " and within the second shell by '.
In general, shell scripts to pass arguments to shell scripts that run shell scripts can be pretty difficult to build. I'd recommend you change your technique a bit to save yourself a lot of effort. Instead of passing the shell commands as command line parameters to the ssh argument, instead provide it through the standard input to the shell. Consider using a pipeline like this, which avoids recursive shell interpretation:
$ echo "echo 1; echo 2; echo 3" | ssh -T xxx
1
2
3
( Here, the -T is just to supress ssh complaining of lack of pseudoterminal).
All the arguments to ssh are combined into a single whitespace-separate string passed to sh -c on the remote end. This means that
ssh sandbox bash -c "echo 1; echo 2; echo 3"
results in the execution of
sh -c 'bash -c echo 1; echo 2; echo 3'
Note the loss of quotes; ssh got the three arguments bash, -c, and echo 1; echo 2; echo 3 after quote removal. On the remote end, bash -c echo 1 just executes echo, with $0 in the shell set to 1.
The command
ssh sandbox bash -c "date; date;"
is treated the same way, but now the first command contains no whitespace. The result on the remote end is
sh -c 'bash -c date; date;'
which means first a new instance of bash runs the date command, followed by the date command being executed directly by sh.
In general, it's a bad idea to use ssh's implicit concatenation. Always pass the command you want executed as a properly escaped single argument:
ssh sandbox 'bash -c "echo 1; echo 2; echo 3"'

Command not found.when executing bash on remote host

Why I cannot execute command on remote host.
Do I miss something?
Bash file: hello.sh
#!/bin/sh
host_name="myHost"
ssh $host_name '
STR="Hello World!"
echo $STR
'
executing above file: the print out:
> ./print_node_status.sh
Enter Windows password:
STR=Hello World!: Command not found.
STR: Undefined variable.
It looks like your remote shell is the C shell, not Bash.
You have several options:
Adapt your code to conform to that shell's language:
ssh $host_name '
set STR="Hello World\!"
echo $STR
'
Execute /bin/bash in your remote process, if it is available, e.g.:
ssh $host_name '
exec /bin/bash
STR="Hello World!"
echo $STR
'
Change the default shell of your user on that node to /bin/bash, see the chsh(1) manpage.

Unable to use local and remote variables within a heredoc or command over SSH

Below is an example of a ssh script using a heredoc (the actual script is more complex). Is it possible to use both local and remote variables within an SSH heredoc or command?
FILE_NAME is set on the local server to be used on the remote server. REMOTE_PID is set when running on the remote server to be used on local server. FILE_NAME is recognised in script. REMOTE_PID is not set.
If EOF is changed to 'EOF', then REMOTE_PID is set and `FILE_NAME is not. I don't understand why this is?
Is there a way in which both REMOTE_PID and FILE_NAME can be recognised?
Version 2 of bash being used. The default remote login is cshell, local script is to be bash.
FILE_NAME=/example/pdi.dat
ssh user#host bash << EOF
# run script with output...
REMOTE_PID=$(cat $FILE_NAME)
echo $REMOTE_PID
EOF
echo $REMOTE_PID
You need to escape the $ sign if you don't want the variable to be expanded:
$ x=abc
$ bash <<EOF
> x=def
> echo $x # This expands x before sending it to bash. Bash will see only "echo abc"
> echo \$x # This lets bash perform the expansion. Bash will see "echo $x"
> EOF
abc
def
So in your case:
ssh user#host bash << EOF
# run script with output...
REMOTE_PID=$(cat $FILE_NAME)
echo \$REMOTE_PID
EOF
Or alternatively you can just use a herestring with single quotes:
$ x=abc
$ bash <<< '
> x=def
> echo $x # This will not expand, because we are inside single quotes
> '
def
remote_user_name=user
instance_ip=127.0.0.1
external=$(ls /home/)
ssh -T -i ${private_key} -l ${remote_user_name} ${instance_ip} << END
internal=\$(ls /home/)
echo "\${internal}"
echo "${external}"
END

Linux bash script: share variable among terminal windows

If I do this:
#!/bin/bash
gnome-terminal --window-with-profile=KGDB -x bash -c 'VAR1=$(tty);
echo $VAR1; bash'
echo $VAR1
How can I get the last line from this script to work? I.e., be able to access the value of $VAR1 (stored on the new terminal window) from the original one? Currently, while the first echo is working, the last one only outputs an empty line.
The short version is that you can't share the variable. There's no shared channel for that.
You can write it to a file/pipe/etc. and then read from it though.
Something like the following should do what you want:
#!/bin/bash
if _file=$(mktemp -q); then
gnome-terminal --window-with-profile=KGDB -x bash -c 'VAR1=$(tty); echo "$VAR1"; declare -p VAR1 > '\'"$_file"\''; bash'
cat "$_file"
. "$_file"
echo "$VAR1"
fi

Resources