Shell error when using multiline ssh command inside user prompt check - linux

Running into a bit of an issue while trying to add a user prompt check to a existing script which was already working.
Here is the existing script, which is working...
#!/bin/sh
echo "**** Pulling changes into Production"
ssh user#example.com "$( cat <<'EOT'
cd example.com/html/ || exit
unset GIT_DIR
git pull
EOT
)"
Here is my modified script with a user prompt, which is broken.. and it is only broken when I add the ssh line. Works perfectly with just echoes.
#!/usr/bin/env bash
while true; do
read -p "Are you sure you want to pull changes into production? [y/N] " yn
case $yn in
[Yy]* )
ssh user#example.com "$( cat <<'EOT'
cd example.com/html/ || exit
unset GIT_DIR
git pull
EOT
)";
exit;;
[Nn]* )
echo "!!!! CANCELLING !!!!";
exit;;
* ) echo "Please answer yes or no.";;
esac
done
The error I'm getting is...
Syntax error: end of file unexpected (expecting ")")

EOT must appear at the beginning of the line. Your EOT appears somewhere in between, with lots of leading spaces.
Replace
EOT
By
EOT

Related

Shell script to scp rpm to remote server and install it. Needs a variable to pass remote host name

I am attempting to write a sort of menu script which will achieve the below once executed:
Ask what user/password you are executing the script as.
Ask the remote server i.p. you want to scp files to.
Ask the directory where the files are stored locally.
Ask the directory where all the files are to be transferred to.
Copy all the files.
The first hurdle I have is getting the password stored as a variable within the menu. I think sshpass will be good for that. I would like to configure a menu like this:
title="Select example"
prompt="Pick an option:"
options=("A" "B" "C")
echo "$title"
PS3="$prompt "
select opt in "${options[#]}" "Quit"; do
case "$REPLY" in
1 ) echo "You picked $opt which is option $REPLY";;
2 ) echo "You picked $opt which is option $REPLY";;
3 ) echo "You picked $opt which is option $REPLY";;
$(( ${#options[#]}+1 )) ) echo "Goodbye!"; break;;
*) echo "Invalid option. Try another one.";continue;;
esac
done
But the menu would ask you for username, local directory of files, i.p. of remote server, remote server directory and then run the scp command it constructed.
Something like this:
password="your password"
username="username"
Ip="<IP>"
sshpass -p "$password" scp /<PATH>/final.txt $username#$Ip:/root/<PATH>
But I'm a bit confused as to how I can put that all of that together so that the menu gathers the required information and then executes the collected input so that it simply constructs an scp command to fire.
Can anyone offer any experience with this to aid me in the construction of the menu script?
Thanks!
You don't need a menu, just ask for each input sequentially.
read -p "Username:" username
read -s -p "Password:" password
read -p "Server IP:" ip
read -p "Local directory:" local
read -p "Remote directory" remote
sshpass -p "$password" scp "/$local/final.txt" "$username#$ip:/root/$remote/"
If you still need a menu here it is. I modified your script a bit to make it construct/print scp command while entering values one by one.
#!/bin/bash
title="Select example"
prompt="Pick an option:"
declare -A options # turn options to an associative array
options[1]='change user name'
options[2]='change user password'
options[3]='change remote host address'
options[4]='change path to source files'
options[5]='change destination path on remote server'
PS3="$prompt "
menu () { # wrap all this to a function to restart it after edition of an element
echo "$title"
select opt in "${options[#]}" "Quit"; do
case "$REPLY" in
1 ) read -p "${options[1]}: " user;;
2 ) read -sp "${options[2]}: " pass;; # read password with option -s to disable output
3 ) read -p "${options[3]}: " addr;;
4 ) read -p "${options[4]}: " from;;
5 ) read -p "${options[5]}: " dest;;
$(( ${#options[#]}+1 )) ) echo "Goodbye!" ; exit;;
*) echo "Invalid option. Try another one.";;
esac
clear # clear screen to remove inputs
printf "Creating scp command:\n"
# we don't want to show password, print *** if password set and 'password not set' if not
[[ $pass ]] && pass2='***' || pass2='[password not set]'
# text in [] will be printed if var is empty or not set, it's usefull to add default values to vars ${var:-default_value}
printf "sshpass -p $pass2 scp -r ${from:-[source not set]}/* ${user:-[user not set]}#${addr:-[host not set]}:${dest:-[destination not set]}/\n\n"
menu
done
}
clear # clear screen
menu # and start menu function
And here I've got a script with similar (but richer) functionality, please take a look.

How to put if statement inside a lftp block

I am writing a bash script to download files from ftp server using lftp. I wanted to delete the files based on the second input argument.
#!/bin/bash
cd $1
lftp -u found,e48RgK7s sftp://ftp.xxx.org << EOF
set xfer:clobber on
mget *.xml
if [ $2 = "prod"]; then
echo "Production mode. Deleting files"
mrm *.xml
else
echo "Non-prod mode. Keeping files"
fi
EOF
However, if statement is not allowed in the lftp block before EOF.
Unknown command `if'.
Unknown command `then'.
Usage: rm [-r] [-f] files...
Unknown command `else'.
How do I embed if statement in such block?
A command substitution will do:
#!/bin/bash
cd "$1" || exit
mode=$2
lftp -u found,e48RgK7s sftp://ftp.xxx.org << EOF
set xfer:clobber on
mget *.xml
$(
if [ "$mode" = "prod" ]; then
echo "Production mode. Deleting." >&2 # this is logging (because of >&2)
echo "mrm *.xml" # this is substituted into the heredoc
else
echo "Non-prod mode. Keeping files" >&2
fi
)
EOF
Note that inside the substitution for the heredoc, we're routing log messages to stderr, not stdout. This is essential, because everything on stdout becomes a command substituted into the heredoc sent to lftp.
Other caveats to command substitution also apply: They run in subshells, so a assignment made inside the command substitution will not apply outside of it, and there's a performance cost to starting them.
A more efficient approach is to store your conditional components in a variable, and expand it inside the heredoc:
case $mode in
prod)
echo "Production mode. Deleting files" >&2
post_get_command='mget *.xml'
;;
*)
echo "Non-production mode. Keeping files" >&2
post_get_command=
;;
esac
lftp ... <<EOF
set xfer:clobber on
mget *.xml
$post_get_command
EOF

Transfer files using lftp in bash script

I have server A test-lx, and server B test2-lx, I want to transfer files from server A to server B.
While transfering the files i'll need to create a driectory only if it's not exist, how can i check if a directory exist during the lftp conenction? How can i out several files in one command instead of doing this in 2 lines.
Is there an option to use find -maxdepth 1 -name DirName
Here is my code:
lftp -u drop-up,1Q2w3e4R ftp://ta1bbn01:21 << EOF
cd $desFolder
mkdir test
cd test
put $srcFil
put $srcFile
bye
EOF
Simple way with ftp:
#!/bin/bash
ftp -inv ip << EOF
user username password
cd /home/xxx/xxx/what/you/want/
put what_you_want_to_upload
bye
EOF
With lftp:
#!/bin/bash
lftp -u username,password ip << EOF
cd /home/xxx/xxx/what/you/want/
put what_you_want_to_upload
bye
EOF
From lftp manual:
-u <user>[,<pass>] use the user/password for authentication
You can use mkdir for create a directory. And you can use put command several time like this:
put what_you_want_to_upload
put what_you_want_to_upload2
put what_you_want_to_upload3
And you can close connection with bye
You can check folder is exist or not like this:
#!/bin/bash
checkfolder=$(lftp -c "open -u user,pass ip; ls /home/test1/test1231")
if [ "$checkfolder" == "" ];
then
echo "folder does not exist"
else
echo "folder exist"
fi
From lftp manual:
-c <cmd> execute the commands and exit
And you can open another connection for put some files.
I don't know how to check folder is exist or not with one connection, but I can do that like this. Maybe you can find better solution:
#!/bin/bash
checkfolder=$(lftp -c "open -u user,pass ip; ls /home/test1/test2")
if [ "$checkfolder" == "" ];
then
lftp -u user,pass ip << EOF
mkdir test2
cd test2
put testfile.txt
bye
EOF
else
echo "The directory already exists - exiting"
fi
I used the same basic coding outline as phe however I found that using ls /foldername will output "folder does not exist" if the folder is empty. To solve this I use
#!/bin/bash
checkfolder=$(lftp -c "open -u user,pass ip; ls | grep /test1231")
if [ "$checkfolder" == "" ];
then
echo "folder does not exist"
else
echo "folder exists"
fi
Please note this only works if the folder is in the root directory. For sub directories in a folder the following should work.
#!/bin/bash
checkfolder=$(lftp -c "open -u user,pass ip; find | grep home/test1/test1231")
if [ "$checkfolder" == "" ];
then
echo "folder does not exist"
else
echo "folder exists"
fi
First prepare credentials record into ~/.netrc file as:
machine site-url-here
login user-login-here
password user-password-here
so you don't have to expose your password on the command line to use this in script.
Then call:
lftp -e "lftp-command-here" ftps://user-login-here#site-url-here/initial-folder-here/`
In my case I run mget -c * lftp command for getting all logs from java spring boot application running linux app instance at azure infrastructure.
Of course you can put your commands separated by semicolon there.

Bash script output not going to stdout

I have a build process, kicked off by Make, that executes a lot of child scripts.
A couple of these child scripts require root privileges, so instead of running everything as root, or everything as sudo, I'm trying to only execute the scripts that need to be as root, as root.
I'm accomplishing this like so:
execute_as_user() {
su "$1" -s /bin/bash -c "$2;exit \$?"
}
Arg $1 is the user to run the script as, arg $2 is the script.
Arg $1 is either root (gotten with: $(whoami) since everything is under sudo), or the current user's account (gotten with: $(logname))
The entire build is kicked off as:
sudo make all
Sample from the Makefile:
LOG="runtime.log"
ROTATE_LOG:=$(shell bash ./scripts/utils/rotate_log.sh)
system:
/bin/bash -c "time ./scripts/system.sh 2>&1 | tee ${LOG}"
My problem is... none of the child scripts are printing output to stdout. I believe it to be some sort of issue with an almost recursive call of su root... but I'm unsure. From my understanding, these scripts should already be outputting to stdout, so perhaps I'm mistaken where the output is going?
To be clear, I'm seeing no output in either the logfile nor displaying to the terminal (stdout).
Updating for clarity:
Previously, I just ran all the scripts either with sudo or just as the logged in user... which with my makefile above, would print to the terminal (stdout) and logfile. Adding the execute_as_user() function is where the issue cropped up. The scripts execute and build the project... just no display "that it's working" and no logs.
UPDATE
Here is some snippets:
system.sh snippet:
execute_script() {
echo "Executing as user $3: $2"
RETURN=$(execute_as_user $3 ${SYSTEM_SCRIPTS}/$2)
if [ ${RETURN} -ne ${OK} ]
then
error $1 $2 ${RETURN}
fi
}
build_package() {
local RETURN=0
case "$1" in
system)
declare -a scripts=(\
"rootfs.sh" \
"base_files.sh" \
"busybox.sh" \
"iana-etc.sh" \
"kernel.sh" \
"firmware.sh" \
"bootscripts.sh" \
"network.sh" \
"dropbear.sh" \
"wireless_tools.sh" \
"e2fsprogs.sh" \
"shared_libs.sh"
)
for SCRIPT_NAME in "${scripts[#]}"; do
execute_script $1 ${SCRIPT_NAME} $(logname)
echo ""
echo -n "${SCRIPT_NAME}"
show_status ${OK}
echo ""
done
# finalize base system
echo ""
echo "Finalizing base system"
execute_script $1 "finalize.sh" $(whoami)
echo ""
echo -n "finalize.sh"
show_status ${OK}
echo ""
# package into tarball
echo ""
echo "Packing base system"
execute_script $1 "archive.sh" $(whoami)
echo ""
echo -n "archive.sh"
show_status ${OK}
echo ""
echo ""
echo -n "Build System: "
show_status ${OK}
;;
*)
echo "$1 is not supported!"
exit 1
esac
}
sample child script executed by system.sh
cd ${CLFS_SOURCES}/
tar -xvjf ${PKG_NAME}-${PKG_VERSION}.tar.bz2
cd ${CLFS_SOURCES}/${PKG_NAME}-${PKG_VERSION}/
make distclean
RESPONSE=$?
if [ ${RESPONSE} -ne 0 ]
then
pkg_error ${RESPONSE}
exit ${RESPONSE}
fi
ARCH="${CLFS_ARCH}" make defconfig
RESPONSE=$?
if [ ${RESPONSE} -ne 0 ]
then
pkg_error ${RESPONSE}
exit ${RESPONSE}
fi
# fixup some bugs with musl-libc
sed -i 's/\(CONFIG_\)\(.*\)\(INETD\)\(.*\)=y/# \1\2\3\4 is not set/g' .config
sed -i 's/\(CONFIG_IFPLUGD\)=y/# \1 is not set/' .config
etc...
Here's the entire system.sh script:
https://github.com/SnakeDoc/LiLi/blob/master/scripts/system.sh
(i know the project is messy... it's a learn-as-you-go style project)
Previously, I just ran all the scripts either with sudo or just as the
logged in user... which with my makefile above, would print to the
terminal (stdout) and logfile. Adding the execute_as_user() function
is where the issue cropped up. The scripts execute and build the
project... just no display "that it's working" and no logs.
Just a guess, but you're probably not calling your function or not calling it properly:
execute_as_user() {
su "$1" -s /bin/bash -c "$2;exit \$?"
}
execute_as_user "$#"
I also noticed that you're not passing any argument to the script at all. Is this meant?
./scripts/system.sh ???

how to hide specific standard output of shell command

I am doing ssh of a "if statement" to a remote server.
Example:
========== Start of Script========
#!/bin/sh
CMD='if [ ! -d "/user/directory" ]; then echo -e "user directory missing"; else echo -e "present"; fi;
ssh remoteserver "$CMD"
==========End of script==========
Query:
While running this script ,I get welcome message from remote server and then the message given by my if condition. I do not wish to receive the welcome message from remote server. What can be done to supress that ?
example:
:/root> ./script.sh
Warning Notice
This is a protected network,and if you are not authorized.....
I think you can try:
ssh -o LogLevel=Error <rest of cmd>
or
ssh remoteserver 'remotecommand args ... 2>&1' 2>/dev/null
which will only removes the welcome message.
You can check other solutions in here
http://www.linuxquestions.org/questions/linux-security-4/how-do-you-turn-off-login-banner-for-non-interactive-ssh-470516/

Resources