Unable to use EOT inside if in shell script - linux

Unable to use End of transmission (EOT) with ssh command inside if, it gives compilation error. I have tried using <<-EOT and <<<EOT but nothing worked. Can anyone suggest a fix for this?
#!bin bash
if [ -z "$2" ];
then
rsync -a abc.tgz root#$1:/var/folder1
echo "Done upload"
# Change permissions of agent image and create image configuration
ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no root#$1<<EOT
chmod 644 /var;
echo "image.id=$containerSha" > /var;
EOT
else
rsync -a abc.tgz root#$1:/var
echo "Upload done"
ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no root#$1<<EOT
cd /var;
sshpass -p '$3' rsync -a abc.tgz root#$2:/var;
sshpass -p '$3' ssh root#$2 'chmod 777 /var/*; ls -ltr';
EOT
fi
exit

Running your script through Shellcheck reveals these errors (along with a misshaped shebang line):
Line 12:
EOT
^-- SC1039 (error): Remove indentation before end token
(or use <<- and indent with tabs).
Line 21:
EOT
^-- SC1039 (error): Remove indentation before end token
(or use <<- and indent with tabs).
Line 23:
exit
^-- SC1072 (error): Here document was not correctly terminated.
Fix any mentioned problems and try again.
The EOT markers must not be indented.

I just had this issue and removing indents will solves the problem:
Instead of:
...
ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no root#$1<<EOT
chmod 644 /var;
echo "image.id=$containerSha" > /var;
EOT
else
...
You can try:
...
ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no root#$1<<EOT
chmod 644 /var;
echo "image.id=$containerSha" > /var;
EOT
else
...

If I'm not mistaken, an EOT is the character with the numeric value 4. If you define (for better readability) in your code
eot=$'\004'
you can later use ${eot} to denote your end-of-transmission.
UPDATE: My answer here refers to the problem of representing the end-of-transmission character in a bash program. As AKX reasonably argued in his comment, the real question is unrelated to end-of-transmission, but on how to mark the end of a here-document.

Related

Shell script to login to remote VM and run commands from remote VM

I need to login to a remote VM and run some commands. I am able to login successfully but echo command does not return list of files from the remote VM instead it returns output from local machine. Can anyone suggest how can I achieve this?
ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no root#$1<<EOF
cd /var;
echo "$(ls)";
EOF
exit
It worked after removing echo. PFB solution:
ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no root#$1 << EOT
cd /var;
ls -ltr;
EOT
exit
You have to escape $ in EOF sequence like this:
ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no root#$1<<EOF
cd /var;
echo "\$(ls)";
EOF
Or escape whole EOF sequence like this:
ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no root#$1<<'EOF'
cd /var;
echo "$(ls)";
EOF
alternatively, if you quote the marker, then substitution doesn't take place in the local shell:
ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no root#$1<<'EOF'
cd /var;
echo "$(ls)";
EOF
exit

Error when stacking SSH command arguments within a bash script using other scripts as variables

I have a csv file called addresses.csv which looks like this,
node-1,xx.xxx.xx.xx,us-central-a
....
node-9,xxx.xx.xxx.xx,us-east1-a
I have a script below called 0run.sh,
#!/bin/bash
username='user'
persist="bash /home/${username}/Documents/scripts/disk/persistentDisk.sh"
first="bash /home/${username}/Documents/scripts/disk/firstAttach.sh"
while IFS=, read -r int ip <&3; do
if [ "$int" == "node-1" ]; then
--->ssh -i ~/.ssh/key -o StrictHostKeyChecking=no -l ${username} ${ip} "${persist}; ${first}"<---
else
ssh -i ~/.ssh/key -o StrictHostKeyChecking=no -l ${username} ${ip} "${first}"
fi
done 3<addresses.csv
The error occurs in the part of the code where I drew the arrows.
When it runs on node-1, instead of running ..persistentDisk.sh followed by ..firstAttach.sh, it only runs ..persistentDisk.sh and gives me the following error before it runs ..persistentDisk.
bash: /home/user/Documents/scripts/disk/firstAttach.sh: No such file or directory
The rest of the script runs completely fine. The only error occurs at this one part where it misses the 2nd script.
When I run the command like this it runs fine.
ssh -i ~/.ssh/key -o StrictHostKeyChecking=no -l ${username} ${ext} "${first}"
When I run it like this, it runs fine as well.
ssh -i ~/.ssh/key -o StrictHostKeyChecking=no -l user xxx.xx.xxx.xx "bash /home/${username}/Documents/scripts/disk/persistentDisk.sh; bash /home/${username}/Documents/scripts/disk/firstAttach.sh"
When I run the command like with a \ before the ; to escape it like this,
ssh -i ~/.ssh/key -o StrictHostKeyChecking=no -l ${username} ${ext} "${persist}\; ${first}"
I get the following error, and neither scripts run within the node-1 part of the code, but the rest of the code's else loops run fine.
bash: /home/user/Documents/scripts/disk/persistentDisk.sh;: No such file or directory
Why can't I stack the 2 commands within the if statement in the ssh using variables?
If I clearly understand: your real problem consist to leave STDIN free for interaction in target host!
About read and redirection
Try using:
#!/bin/bash
username='user'
persist="bash /home/${username}/Documents/scripts/disk/persistentDisk.sh"
first="bash /home/${username}/Documents/scripts/disk/firstAttach.sh"
while IFS=, read -r -u $list int ip foo; do
if [ "$int" == "node-1" ]; then
echo CMD... $ip, $persist
else
[ "$ip" ] && echo CMD... $ip, $first
fi
done {list}<addresses.csv
Tested, this èroduce:
CMD... xx.xxx.xx.xx, bash /home/user/Documents/scripts/disk/persistentDisk.sh
CMD... xxx.xx.xxx.xx, bash /home/user/Documents/scripts/disk/firstAttach.sh
-u flag to read, tell to use file descriptor ${list} instead of STDIN
foo is some useless variable used to prevent rest of line to be stored in $ip (xx.xxx.xx.xx,us-central-a in this case)
{list}</path/to/filename create a new variable by finding any free file descriptor.
About ssh (and redirection)
You could use:
#!/bin/bash
username='user'
persist="/home/${username}/Documents/scripts/disk/persistentDisk.sh"
first="/home/${username}/Documents/scripts/disk/firstAttach.sh"
while IFS=, read -r -u $list int ip foo; do
[ "$int" = "node-1" ] && cmd=persist || cmd=first
[ "$ip" ] && ssh -i ~/.ssh/key -t -o StrictHostKeyChecking=no \
-l ${username} ${ext} /bin/bash "${!cmd}"
done {list}<addresses.csv
By using this syntax, you will keep STDIN free for script running on target host.

How to log non-interactive bash command sent through ssh

I'm sending a command through ssh:
ssh server.org 'bash -s' << EOF
ls -al
whoami
uptime
EOF
How to log it in the system (remote server)? I'd like to log those commands in some file (.bash_history or /tmp/log).
I've tried to add the line below to sshd_config:
ForceCommand if [[ -z $SSH_ORIGINAL_COMMAND ]]; then bash; else echo "$SSH_ORIGINAL_COMMAND" >> .bash_history; bash -c "$SSH_ORIGINAL_COMMAND"; fi
But it logs "bash -s" only.
I'll appreciate any help.
When bash shell exits, bash reads and executes commands from the ~/.bash_logout file. Probably you can run the history command at the end in the .bash_logout(of the server) and save it to some location.
If it suffices to work with the given command, we can put the necessary additions to enable and log command history at the beginning and end, e. g.
ssh server.org bash <<EOF
set -o history
ls -al
whoami
uptime
history|sed 's/ *[0-9]* *//' >>~/.bash_history
EOF
Or we could put them into the awfully long ForceCommand line:
… if [[ "$SSH_ORIGINAL_COMMAND" == bash* ]]; then echo "set -o history"; cat; echo "history|sed 's/ *[0-9]* *//' >>~/.bash_history"; else cat; fi | bash -c "$SSH_ORIGINAL_COMMAND"; fi

Bash script does not ssh all the entries of a csv file

I am trying to patch a bunch of CENT OS machines with the latest fix pack. I have the below bash script that takes csv file as a input which has the ip address and password for those machines.
The code works fine however, it would only work for the first row it does not seem to be working for the rest of the list as my output.txt only has the entry only for the first row host .
patch.sh
INPUT=hosts_test.cvs
OLDIFS=$IFS
IFS=,
[ ! -f $INPUT ] && { echo "$INPUT file not found"; exit 99; }
while read privateip password
do
sshpass -p$password ssh -t -o "StrictHostKeyChecking no" user123#$privateip "
hostname
hostname -I --all-ip-addresses
sudo yum -y update bash
env x='() { :;}; echo vulnerable' bash -c \"echo If you see the word vulnerable above, then you are vulnerable to shellshock\"
echo ""
exit
" >> output.txt
done < $INPUT
IFS=$OLDIFS
hosts_test.cvs
10.xxx.xx.219,abcd~qY1
10.xxx.xx.226,l4~abcdefg
10.xxx.xx.221,l4#abcdefgh
Terminal Output
Pseudo-terminal will not be allocated because stdin is not a terminal.
Add at the end of your sshpass command </dev/null.
Add Defaults:username !requiretty to your /etc/sudoers config
Get rid of -t from your ssh command
Optional, but recommended: set up public key auth so you don't have your passwords lying around in text files.
You can pass ssh another -t to force pty allocation:
ssh -t -t

bash script command line parameters beginning with hyphen are ignored

I've written a VERY simple bash script to sync files, but I can't get the script to read the parameters correctly when the parameters are: -e "ssh -p 1234". I realized I need to escape the quotes to get those to display properly, so: -e \"ssh -p 1234\", but the -e is ignored. I tried quoting it, which gets it to show up if I echo out $option, but it still doesn't work properly. Any advice to what I'm doing wrong would be greatly appreciated.
#!/bin/bash
host=`hostname -s`
if [ "$host" = "machine1" ]; then
transfer_to="machine2";
else
transfer_to="machine1";
fi
# use $option when passing in. i.e --delete, -e "ssh -p 1234"
option="$#"
rsync -va $option /var/sync_dir/ $transfer_to:/var/sync_dir --progress
You should either use $# directly or copy it to an array:
rsync -va "$#" /var/sync_dir/ $transfer_to:/var/sync_dir --progress
or
option=("$#")
rsync -va "${option[#]}" /var/sync_dir/ $transfer_to:/var/sync_dir --progress

Resources