Bash command substitution on remote host [duplicate] - linux

This question already has answers here:
How to cat <<EOF >> a file containing code?
(5 answers)
Closed 7 years ago.
I'm trying to run a bash script that ssh's onto a remote host and stops the single docker container that is running.
#!/usr/bin/env bash
set -e
ssh <machine> <<EOF
container=$(docker ps | awk 'NR==2' | awk '{print $1;}')
docker stop $container
EOF
However, I get the following error:
stop.sh: line 4: docker: command not found
When I do this manually (ssh to the machine, run the commands) all is fine, but when trying to do so by means of a script I get the error. I guess that my command substitution syntax is incorrect and I've searched and tried all kinds of quotes etc but to no avail.
Can anyone point me to where I'm going wrong?

Use <<'EOF' (or <<\EOF -- quoting only the first character will have the same effect) when starting your heredoc to prevent its expansions from being evaluated locally.
BTW, personally, I'd write this a bit differently:
#!/bin/sh -e
ssh "$1" bash <<'EOF'
{ read; read container _; } < <(docker ps)
docker stop "$container"
EOF
The first read consumes the first line of docker ps output; the second extracts only the first column -- using bash builtins only.

Related

How to handle quotes, backtick special characters for running linux bash shell command in remote server

Someone, please help me in correcting below command I wasted more than a day fixing below but failed, please help, I will be using below in ansible shell module.
ssh -o ConnectTimeout=5 splunk#10.145.32.172 '
sdline="`
grep -n TA-aws-hf-{{client_code}}-{{env_name}} /opt/splunk/etc/system/local/serverclass.conf
| awk -F \":\" \'{print $1}\'
`
&& sed -ie \"$sdline,`
echo $sdline + 3
| bc
`d\" /opt/splunk/etc/system/local/serverclass.conf
"
> ^C
Even tried below way:
ssh -o ConnectTimeout=5 splunk#10.145.32.172 exec sdline=`grep -n TA-aws-hf-{{client_code}}-{{env_name}} /opt/splunk/etc/system/local/serverclass.conf|awk -F ":" '{print $1}'` && sed -ie "$sdline,`echo $sdline + 3|bc` d" /opt/splunk/etc/system/local/serverclass.conf
grep: /opt/splunk/etc/system/local/serverclass.conf: No such file or directory
bash: line 0: exec: sdline=: not found
Context: It seems this question originated as an XY Problem. OP appears to want to remove the 3 lines including and after the string "TA-aws-hf-{{client_code}}-{{env_name}}".
Backticks are deprecated; use $(modern $(command) substitution) when necessary. It is not necessary in this case.
If your remote server has GNU sed:
ssh splunk#10.145.32.172 'sed -i "/TA-aws-hf-{{client_code}}-{{env_name}}/,+2d" /opt/splunk/etc/system/local/serverclass.conf'
If that gives you sed: -e expression #1, char 19: unexpected ',':
ssh splunk#10.145.32.172 '
cd /opt/splunk/etc/system/local
awk "/TA-aws-hf-{{client_code}}-{{/ {i=-3} i++>0" \
serverclass.conf > temp && mv $_ serverclass.conf
'
Your remote command is quite complicated.
I suggest the following:
Use ssh to gain interactive shell in 10.145.32.172
Create a script on 10.145.32.172 that do the work, with everything hard coded.
Refactor command line parameters to your script.
Call your script remotely from your local machine.
This strategy simplify the script and its maintenance. Allowing you to send only the important parameters.
If you have to deploy the script on many remote machines. Use shared storage resources, like NFS. Optionally copy the script using scp prior to running it.

How do you export local shell variables into a multi-command ssh?

I am trying to ssh to another server in a shell script and run some scripts.
Currently my line looks something like:
ssh user#$SERVER '$(typeset -a >> /dev/null); PROFILE_LOCATION=`locate db2profile| grep -i $INST_NAME| grep -v bak`; . $PROFILE_LOCATION; function1; function2;'
I've tried both ' and " , as well as using a combination of those with \; or ';'
How do I use the variables I have in my current shell script in my ssh into another server and running multiple commands? Thanks!!
If you want function declarations, and your shell is bash, use typeset -p rather than typeset -a (which will provide a textual dump of variables but not functions). Also, you need to actually run that in a context where it'll be locally evaluated (and ensure that your remote shell is something that understands it, not /bin/sh).
The following hits all those points:
evaluate_db2profile() {
local db2profile
db2profile=$(locate db2profile | grep -i "$INST_NAME" | grep -v bak | head -n 1)
[ -n "$db2profile" ] && . "$db2profile"
}
ssh "user#$SERVER" bash -s <<EOF
$(typeset -p)
evaluate_db2profile
function1
function2
EOF
Because <<EOF is used rather than <<'EOF', the typeset -p command is run locally and substituted into the heredoc. (You could also accomplish this by using double rather than single quotes in the one-line formulation, but see below).
Defining evaluate_db2profile locally as a function ensures that typeset -p will emit it in a format that the remote shell can evaluate, without need to be concerned about escaping.
Using bash -s on the remote command line ensures that the shell interpreting your functions is bash, not /bin/sh. If your code is written for ksh, run ksh -s to achieve that same effect.

Assigning variables inside remote shell script execution over SSH [duplicate]

This question already has answers here:
is it possible to use variables in remote ssh command?
(2 answers)
Closed 6 years ago.
I am trying to execute some shell script on a remote server via SSH.
Given below is a code sample:
ssh -i $KEYFILE_PATH ubuntu#$TARGET_INSTANCE_IP "bash" << EOF
#!/bin/bash
cat /home/ubuntu/temp.txt
string=$(cat /home/ubuntu/temp.txt )
echo $string
EOF
cat prints the expected result but
$string prints nothing.
How do I store the return value of cat in a variable?
You need to make the content of Here doc literal otherwise they will be expanded in the current shell, not in the desired remote shell.
Quote EOF:
ssh .... <<'EOF'
...
...
EOF
You should be able to simply do this:
ssh -i $KEYFILE_PATH ubuntu#$TARGET_INSTANCE_IP "bash" <<'_END_'
cat /home/ubuntu/temp.txt
string=$(cat /home/ubuntu/temp.txt)
echo $string
_END_
<<'_END_' ... _END_ is called a Here Document literal, or "heredoc" literal. The single quotes around '_END_' prevent the local shell from interpreting variables and commands inside the heredoc.
The intermediate shell is not required (assuming you use bash on the remote system).
Also you dont have to use an intermediate HERE-DOC. Just pass a multiline Command:
ssh -i $KEYFILE_PATH ubuntu#$TARGET_INSTANCE_IP '
cat /home/ubuntu/temp.txt
string=$(cat /home/ubuntu/temp.txt )
echo $string
'
Note I am using single quotes to prevent evaluation on the local shell.

bash: can't output: ps -ef|grep to variable [duplicate]

This question already has answers here:
Prevent bash from interpreting without quoting everything
(3 answers)
Closed 7 years ago.
I'm trying to insert the result of a command to variable using a Bash script on a Unix platform.
I need to use the next command: ps -ef|grep <my process>, but when I run it I got an error:
-bash: line 1: 5420: command not found
This is my code:
#!/bin/bash
ps_result=$(ps -ef|grep exe)
echo "$ps_result"
I thing this is some syntax problem because the 'grep' command is colored in yellow, but I can't to fix it (I tried using back-quotes `…` but it didn't work either).
edited:
There is the debug output relevant for the part of the code:
+ ssh ######
++ ps -ef
++ grep exe
Pseudo-terminal will not be allocated because stdin is not a terminal.
Warning: Permanently added '#####' (RSA) to the list of known hosts.
-bash: line 1: 5447: command not found
Here is the full code:
#!/bin/bash
ssh ##### << EOF
ps_result=$(ps -ef|grep exe)
echo "$ps_result"
EOF
I need the ps_result in order to do manipulations in it later.
* the ##### is edit here and not in the original code...
Preliminary versions of the question
The initial version of the question had the code:
ps_result= $(ps -ef|grep exe)
echo $ps_result
This has a space after the assignment, which causes trouble.
Preliminary answer
No spaces around assignments in shell:
ps_result=$(ps -ef|grep exe)
echo "$ps_result"
What you had ps_result= $(ps -ef|grep exe) runs ps | grep, and captures the output. It uses the first word of the output as a command name (nikitag, it seems) and runs that with the rest of the words in the output as arguments and with the environment variable ps_result set to an empty string. And since you don't have a command called nikitag, it fails.
Be very, very, very careful with spaces in shell scripts.
After update to include ssh and here document
Now that the question has been updated to show what's really going on, it is relatively simple to explain the problem. There's a here document and ssh involved too:
#!/bin/bash
ssh ##### << EOF
ps_result=$(ps -ef|grep exe)
echo "$ps_result"
EOF
Because the EOF is not quoted, the ps command is executed on the local machine before the ssh is started, and the output of the command is put into the here document. You need to quote the EOF at the start of the here document so that the document is transferred verbatim and executed on the remote machine:
#!/bin/bash
ssh ##### << 'EOF'
ps_result=$(ps -ef|grep exe)
echo "$ps_result"
EOF
Like the trace shows, the problem is not in the code you posted, but in the code which calls this code.
A here document without quoting will interpolate the process substitutions locally before passing the script to ssh. Use single quotes around the here document terminator after << to fix that.
#!/bin/bash
ssh ##### <<'EOF' # Notice quotes here
ps_result=$(ps -ef|grep exe)
echo "$ps_result"
EOF
The code should be no need of spaces
#!/bin/bash
ps_result=`ps -ef|grep exe`
echo $ps_result

Bash cat on the last created remote file [duplicate]

This question already has answers here:
Shell Script error: "head: invalid trailing option -- 1"
(3 answers)
Closed 8 years ago.
I am trying to recover the content of the last created file on a remote server.
When connected to the remote server I do this:
cat `ls -t /mypath/*.csv | head -1`
CMD="cat `ls -t /mypath/*.txt | head -1`"
But when I try to use the same command:
ssh#XX.XX.XX.XX $CMD
I get an error: ls cannot access /mypath/*.csv No such file or directory.
The ` is forcing to execute the ls on the local system on not the remote.
Is there another way?
Thank you
Your command is failing is because the backticks in $CMD are expanded locally when you create the variable, rather than being expanded on the remote side. So ssh#XX.XX.XX.XX $CMD is actually going to look something like ssh#XX.XX.XX.XX "cat /mypath/local_file" (and local_file may not exists on the remote host, and is probably not the file you want).
You can prevent this local expansion by providing the command directly to ssh.
ssh user#host 'cat /mypath/$(ls -t /mypath/*.txt | head -1)'
ls returns the pathname relative to the directory so you will also need to include the path of the base directory /mypath/ in your cat invocation. To avoid this hardcoding pass the -d flag to ls.
ssh user#host 'cat $(ls -dt /mypath/*.txt | head -1)'

Resources