Multiline SSH remote script - bash

I have been trying for 2 hours to rewrite this SSH remotelly executed command to multiline form to be more readable, but everytime I have something wrong. I tried multiple ways as described in similar questions but I just suck at BASH. I am trying to do
echo "Deploying $1 to remote"
sudo ssh -i ../keys/key.pem username#$2 '
id=$(docker ps -a -q -f name=$1); if [ -n "$id" ]; then docker rm --force $1; fi;
docker run -d --network host -v /var/log/$1/:/var/log/$1/
-e SERVER_PORT=80
--name $1 username/image:$1'
I want to run docker image remotelly with some arguments created remotelly ($id) and some expanded locally before executing the script ($1, $2).

You will probably find this easier to write if you write it as a standalone script:
#!/bin/sh
container="$1"
id=$(docker ps -aq -f name="$container")
if [ -n "$id" ]; then
...
fi
...
Run this by hand on the target system to make sure it does what you want.
Once you have that, you can copy the script and run it in two separate commands:
echo "Deploying $1 to remote"
scp -i ../keys/key.pem launch-container.sh "username#$2:/tmp/launch-container.sh"
ssh -i ../keys/key.pem "username#$2" sh /tmp/launch-container.sh "$1"
You also might look into various system-automation tools that are purpose-built for this kind of task. I'm partial to Ansible for not requiring a dedicated server and working over ssh in the same way you're showing; it has a set of Docker-related commands that can do this set of tasks fairly directly.

Related

how can i expect ssh and run shell command?

i want to mkdir at remote machine, but i dont know if the dir exists, how can i do this?
i use spawn ssh username#ip bash -c [ -d $dest_file ] && echo ok || mkdir -p $dest_file
and returns
while executing
"-d $dest_file "
invoked from within
"[ -d $dest_file ] && echo ok || mkdir -p $dest_file"
(file "mkdir.exp" line 22)
i cant use ssh-key because my ip is dynamic
First, you probably don't need bash -c because ssh is already executing the command with your remote shell.
Secondly, you're not sufficiently quoting your ssh arguments. You're writing an expect script, which uses the tcl programming language, and [ is a special character that will attempt to evaluate its contents as a tcl command and return the output (read more here). For this to work properly, you would need to escape the opening [ to get tcl to interpret it literally:
spawn ssh localhost \[ -d $dest_file ] && echo ok || mkdir -p $dest_file
This seems to work correctly on my system, but as I indicate in a comment it would be much easier to drop all the conditionals and just run:
spawn ssh localhost mkdir -p $dest_file
This accomplishes the same thing and doesn't run afoul of any quoting issues.

Multiline ssh command with for

I'm having a bash script that is executing commands through ssh.
FILENAMES=(
"export_production_20200604.tgz"
"export_production_log_20200604.tgz"
"export_production_session_20200604.tgz"
"export_production_view_20200604.tgz"
)
sshpass -p $PASSWORD ssh -T $LOGIN#$IP '/bin/bash' <<EOF
for f in "${FILENAMES[#]}"; do
echo Untar "$f"
done
EOF
The thing is when I execute the script, $f is empty.
I've looked at multiple solutions online to perform multiple command executions, but none works :
link 1
link 2
...
Could you help me figure it out ?
Note :
The execution of :
for f in "${FILENAMES[#]}"; do
echo Untar "$f"
done
outside the <<EOF EOF, works
On local :
bash 4.4.20(1)-release
Remote :
bash 4.2.46(2)-release
EDIT : Tricks
Having a tight timeline, and having no choice, I implemented the solution provided by #hads0m, may it helps fellow developer having the same issue :
# $1 the command
function executeRemoteCommand() {
sshpass -p $DB_PASSWORD ssh $DB_LOGIN#$DB_SERVER_IP $1
}
for i in "${!FILENAMES[#]}"; do
f=$FILENAMES[$i]
DB_NAME=$DB_NAMES[$i]
# Untar the file
executeRemoteCommand '/usr/bin/tar xzvf '$MONGODB_DATA_PATH'/'$TMP_DIRECTORY'/'$f' --strip-components=1'
# Delete the tar
executeRemoteCommand 'rm -f '$MONGODB_DATA_PATH'/'$TMP_DIRECTORY'/'$f''
# Restore the database
executeRemoteCommand 'mongorestore --host 127.0.0.1:'$DB_PORT' --username "'$MONGODB_USER'" --password "'$MONGODB_PASSWORD'" --authenticationDatabase admin --gzip "'$DB_NAME'" --db "'$DB_NAME'"'
done
You need to escape $ sign to avoid it being expanded locally and pass the array to remote.
This may be what you wanted :
#!/usr/bin/env bash
FILENAMES=(
"export_production_20200604.tgz"
"export_production_log_20200604.tgz"
"export_production_session_20200604.tgz"
"export_production_view_20200604.tgz"
)
sshpass -p $PASSWORD ssh -T $LOGIN#$IP '/bin/bash' <<EOF
$(declare -p FILENAMES)
for f in "\${FILENAMES[#]}"; do
echo Untar "\$f"
done
EOF
Try running it like this:
for f in "${FILENAMES[#]}"; do
sshpass -p $PASSWORD ssh -T $LOGIN#$IP echo Untar "$f"
done
Also, don't forget to add #!/bin/bash into the first line of your script.

Try to connect via script to multiple servers

I am trying to connect to multiple servers from file (servers.txt --> something around 300 IP add) and some server is RHEL5, some RHEL7 (so I must use a diff command).
I can connect to multiple server, its OK, but I can't continue with some condition: like if you don't know one command use another command.
#!/bin/bash
for host in $(cat servers.txt); do
ssh -q -o StrictHostKeyChecking=no root#$host
#when I try to continue with "if" -> of course I am logout from servers
I agree with #rkosegi that this can be achieved an Ansible ad-hoc command, but you'd have to convert the list of servers to an inventory - a very simple task.
At the bash prompt, I think I understand what you want. You want to try multiple commands via each ssh command. So let's assume you want to check the version of the redhat-release package and take different actions from that:
while read HOST; do
ssh -q -o StrictHostKeyChecking=no root#$HOST '
VERSION="`rpm -q --queryformat "%{VERSION}" redhat-release`"
if [[ $VERSION == 5* ]]; then # RHEL5
echo this is a rhel5
elif [[ $VERSION == 7* ]]; then # RHEL7
echo this is a rhel7
else
echo this is neither a rhel5 or rhel7
fi
'
done <servers.txt
Of course, the script can be written on one line, but I thought I'd format it nicer here for increased readability.
Note: The post being tagged with bash, the above command also uses [[ tests which are specific to bash and won't work on some other shells.

Script stuck during read line when script is executed remotely

I want to have one script which starts a services in another server.
I have tested that the script works as expected in the server where the server is going to run.
This is the code which starts the service and monitors the log until it is in the startup process:
pkill -f "$1"
nohup java -jar -Dspring.profiles.active=$PROFILE $1 &
tail -n 0 -f nohup.out | while read LOGLINE
do
echo $LOGLINE
[[ "${LOGLINE}" == *"$L_LOG_STRING"* ]] && pkill -P $$ tail
done
This works fine as long as I execute that from that machine.
Now I want to call that script from another server:
#!/usr/bin/env bash
DESTINATION_SERVER=$1
ssh root#$DESTINATION_SERVER /bin/bash << EOF
echo "Restarting first service..."
/usr/local/starter.sh -s parameter
echo "Restarting second service..."
/usr/local/starter.sh -s parameter2
EOF
Well, everytime I try that the script of the remote server gets stuck in the "while READ" loop. But as I said, when I execute it locally from the server works fine, and in my "not simplified script" I´m not using any system variable or similar.
Update: I just tried to simplify the code even more with the following lines in the first scenario:
pkill -f "$1"
nohup java -jar -Dspring.profiles.active=$PROFILE $1 &
tail -n 0 -f nohup.out | sed "/$L_LOG_STRING/ q"
I'd say the problem is some how in the "|" through ssh, but I still can find why.
it seems that the problem comes from not having an interactive console when you execute the ssh command, therefore the nohup command behaves strangly.
I could solve it in two ways, outputing the code to the file explicitly:
"nohup java -jar -Dspring.profiles.active=test &1 >> nohup.out &"
instead of:
"nohup java -jar -Dspring.profiles.active=test &1&"
Or changing the way I access via ssh adding the tt option (just one did not work):
ssh -tt root#$DESTINATION_SERVER /bin/bash << EOF
But this last solution could lead to other problems with some character, so unless someone suggests another solution that is my patch which makes it work.

Not able use lftp commands running from shellscript

I'm using set of commands from a shellscript. First command is running fine but it is moving to lftp command prompt and expecting manual input instead of running commands from shelscript. Following are the commands i'm using
lftp -e "$HOST"
lftp -u "$USER,$PWD"
lftp -e "cd /inbox"
put $file
bye
Please suggest me some solution
Using lower-case variable names to avoid conflicts with local environment variables or shell-builtins ($USER and $PWD are both builtins, so you shouldn't be setting them yourself):
lftp \
-e "cd /inbox; put $file" \
-u "$user,$pwd" \
"$host"
The point, here, is invoking lftp only once, and passing all the necessary commands to that single invocation.

Resources