Error with Bash script and ssh - bash

I have a bash script where I ssh to a remote host and then create a file depending on the operating system (case statement in bash). When I execute this code on OS X, I expect the value Darwin to be evaluated and the file eg2.txt to be created. However, for some reason the evaluation fails to choose Darwin and it selects * and then creates the file none.txt. Has anyone run into a similar issue? Can someone tell what is wrong?
#!/bin/bash
ssh -l user $1 "cd Desktop;
opname=`uname -s`;
echo \"first\" > first.txt
case \"$opname\" in
"Darwin") echo \"Darwin\" > eg2.txt ;;
"Linux") sed -i \"/$2/d\" choice_list.txt ;;
*) touch none.txt ;;
esac"
P.S. I am running this code primarily on a Mac.

The problem is that your $opname variable is being expanded (into the empty string) by the Bash instance that's running ssh (i.e., on the client-side), rather than being passed over SSH to be handled by the Bash instance on the server-side.
To fix this, you can either use single-quotes instead of double-quotes:
#!/bin/bash
ssh -l user $1 'cd Desktop;
opname=`uname -s`;
echo "first" > first.txt
case "$opname" in
Darwin) echo "Darwin" > eg2.txt ;;
Linux) sed -i "/$2/d" choice_list.txt ;;
*) touch none.txt ;;
esac'
or else you can quote your $ using \:
#!/bin/bash
ssh -l user $1 "cd Desktop;
opname=`uname -s`;
echo \"first\" > first.txt
case \"\$opname\" in
"Darwin") echo \"Darwin\" > eg2.txt ;;
"Linux") sed -i \"/\$2/d\" choice_list.txt ;;
*) touch none.txt ;;
esac"

Related

Execute local script on remote host by passing remote host parameters on command line together with script arguments

is anybody aware if there is a syntax to pass a remote host parameters (user and IP/hostname) together with script arguments on local host and make it execute on the remote host?
I'm not meaning like this: $ ssh user#remoteServer "bash -s" -- < /path/script.ssh -a X -b Y
I want instead for the script to be able to be passed like this: $/path/script.ssh user#remoteServer -a X -b Y
But I'm not sure how to achieve, in the script, this kind of behaviour:
[...] script [...]
connect to user#remoteServer
[...] execute the script code (on the remote host) [...]
end of script
Any suggestion? Do I need to work this from another way instead?
EDIT
I've managed to make the script execute something after it connects via SSH, but I'm a bit as for why some commands are executed before they are passed to the remote host terminal; my code looks like this at the moment:
while getopts 'ha:u:d:s:w:c:' OPT; do
case $OPT in
a) host=$OPTARG;;
u) user=$OPTARG ;;
d) device=$OPTARG ;;
s) sensor=$OPTARG ;;
w) warn_thresh=$OPTARG ;;
c) crit_thresh=$OPTARG ;;
h) print_help
*) printf "Wrong option or value\n"
print_help
esac
done
shift $(($OPTIND - 1))
# Check if host is reachable
if (( $# )); then
ssh ${user}#${host} < $0
# Check for sensor program or file
case $device in
linux) do things
raspberry) do things
amlogic) do things
esac
# Read temperature information
case $device in
linux) do things
raspberry) do things
amlogic) do things
esac
# Check for errors
if (())
then
# Temperature above critical threshold
# Check for warnings
elif (())
then
# Temperature above warning threshold
fi
# Produce Nagios output
printf [......]
fi
The script seemingly runs without issue, but I get no output.
A simplistic example -
if (( $# )) # if there are arguments
then ssh "$1" < $0 # connect to the first and execute this script there
else whoami # on the remote, there will be no args...
uname -n # if remote needs arguments, change the test condition
date # these statements can be as complex as needed
fi
My example script just takes a target system login as its first argument.
Run it with no args it outputs the data for the current system; use a login, it runs there.
If you have password-less logins with authorized keys it's very smooth, otherwise it will prompt you.
Just parse your arguments and behave accordingly. :)
If you need arguments on the remote, use a more complex test to decide which branch to take...
Edit 2
I repeat: If you need arguments on the remote, use a more complex test to decide which branch to take...
while getopts 'ha:u:d:s:w:c:' OPT; do
case $OPT in
a) host=$OPTARG;;
u) user=$OPTARG ;;
d) device=$OPTARG ;;
s) sensor=$OPTARG ;;
w) warn_thresh=$OPTARG ;;
c) crit_thresh=$OPTARG ;;
h) print_help
*) printf "Wrong option or value\n"
print_help
esac
done
shift $(($OPTIND - 1))
# handoff to remote host
if [[ -n "$host" ]]
then scp "${user}#${host}:/tmp/" "$0"
ssh "${user}#${host}" "/tmp/${0##*/} -d $device -s $sensor -w $warn_thresh -c $crit_thresh"
exit $?
fi
# if it gets here, we're ON the remote host, so code accordingly
# Check for sensor program or file
case $device in
linux) do things
raspberry) do things
amlogic) do things
esac
# Read temperature information
case $device in
linux) do things
raspberry) do things
amlogic) do things
esac
# Check for errors
if (())
then
# Temperature above critical threshold
# Check for warnings
elif (())
then
# Temperature above warning threshold
fi
# Produce Nagios output
printf [......]
fi

How to change name of file if already present on remote machine?

I want to change the name of a file if it is already present on a remote server via SSH.
I tried this from here (SuperUser)
bash
ssh user#localhost -p 2222 'test -f /absolute/path/to/file' && echo 'YES' || echo 'NO'
This works well with a prompt, echoes YES when the file exists and NO when it doesn't. But I want this to be launched from a crontab, then it must be in a script.
Let's assume the file is called data.csv, a condition is set in a loop such as if there already is a data.csv file on the server, the file will be renamed data_1.csv and then data_2.csv, ... until the name is unique.
The renaming part works, but the detection part doesn't :
while [[ $fileIsPresent!='false' ]]
do
((appended+=1))
newFileName=${fileName}_${appended}.csv
remoteFilePathname=${remoteFolder}${newFileName}
ssh pi#localhost -p 2222 'test -f $remoteFilePathname' && fileIsPresent='true' || fileIsPresent='false'
done
always returns fileIsPresent='true' for any data_X.csv. All the paths are absolute.
Do you have any idea to help me?
This works:
$ cat replace.sh
#!/usr/bin/env bash
if [[ "$1" == "" ]]
then
echo "No filename passed."
exit
fi
if [[ ! -e "$1" ]]
then
echo "no such file"
exit
fi
base=${1%%.*} # get basename
ext=${1#*.} # get extension
for i in $(seq 1 100)
do
new="${base}_${i}.${ext}"
if [[ -e "$new" ]]
then
continue
fi
mv $1 $new
exit
done
$ ./replace.sh sample.csv
no such file
$ touch sample.csv
$ ./replace.sh sample.csv
$ ls
replace.sh
sample_1.csv
$ touch sample.csv
$ ./replace.sh sample.csv
$ ls
replace.sh
sample_1.csv
sample_2.csv
However, personally I'd prefer to use a timestamp instead of a number. Note that this sample will run out of names after 100. Timestamps won't. Something like $(date +%Y%m%d_%H%M%S).
As you asked for ideas to help you, I thought it worth mentioning that you probably don't want to start up to 100 ssh processes each one logging into the remote machine, so you might do better with a construct like this that only establishes a single ssh session that runs till complete:
ssh USER#REMOTE <<'EOF'
for ((i=0;i<10;i++)) ; do
echo $i
done
EOF
Alternatively, you can create and test a bash script locally and then run it remotely like this:
ssh USER#REMOTE 'bash -s' < LocallyTestedScript.bash

How do you add a .txt file to a shell script as a variable

Hello guy I am trying to write a basic shell script that adds, creates or lists multiple user accounts from a provide list in the form of a file specified at the command line. I am very new to this and have been banging my head on the keyboard for the last few hours. below is an example of the syntax and the code so for. (I called this script buser)
./buser.sh -a userlist (-a is the option and userlist is the filename, it is only an example)
file=$(< `pwd`/$2)
while :
do
case $1 in
-a)
useradd -m "$file"
break
;;
--add)
useradd -m "$file"
break
;;
--delete)
userdel -r "$file"
break
;;
-d)
userdel -r "$file"
break
;;
-l)
cat /etc/passwd | grep "$file"
break
;;
--list)
cat /etc/passwd | grep "$file"
break
;;
esac
done
when the useradd command reads $file it reads all the names as a single line and I get an error.
any help would be greatly appreciated thank you.
Not sure if I understood correctly.
But assuming you have a file with the following content:
**file.txt**
name1
name2
name3
You would like to call buser.sh -a file.txt and run useradd to name1, name2 and name3? I'm also assuming you're using Linux and useradd is the native program, if so I suggest to read the man, because it does not support to add a list of user at once (https://www.tecmint.com/add-users-in-linux/)
You have to call useradd multiple times instead.
while read user;
do
useradd -m $user
done <$2
A few simplifications, plus an error handler if the option doesn't exist:
while read file ; do
case "$1" in
-a|--add)
useradd -m "$file"
;;
-d|--delete)
userdel -r "$file"
;;
-l|--list)
grep -f `pwd`/"$2" /etc/passwd
break
;;
*)
echo "no such option as '$1'..."
exit 2
;;
esac
done < `pwd`/"$2"
Note: the above logic is a bit redundant... case "$1" keeps doing the same test, (with the same result), every pass. OTOH, it works, and it's less code than a while loop in each command list.
You can use sed to create the commands, and eval to run them:
var=$( sed -e 's/^/useradd -m /' -e 's/$/;/' $file )
eval "$var"
(Edited to put in the -m flag.)

Bash script CAT command uses the script parameters rather than leaving $1 in the cat'd file

I am having a bit of an issue with a bash script when trying to cat a new file.
#!/bin/bash
#sudo vim /etc/init.d/glassfish
sudo cat > /etc/init.d/glassfish <<EOF
# Set path variable
GLASSFISH_HOME=/opt/glassfish3
# Establish Commands
case "$1" in
start)
${GLASSFISH_HOME}/bin/asadmin start-domain domain1
;;
stop)
${GLASSFISH_HOME}/bin/asadmin stop-domain domain1
;;
restart)
${GLASSFISH_HOME}/bin/asadmin stop-domain domain1
${GLASSFISH_HOME}/bin/asadmin start-domain domain1
;;
*)
echo "usage: $0 {start|stop|restart}"
;;
esac
exit 0
EOF>
However, when I run this script it replaces the $1 and $0 with what I used to call the script that runs the command, so $1 becomes "" and $0 becomes testscript.sh
Is there any way to prevent this?
If the here document delimiter is entirely unquoted, the contents are treated as a double-quoted string. Quote at least one character of the delimiter (it's simplest to just quote the whole thing) to have the here document treated as a single-quoted string, preventing parameter expansion.
sudo tee /etc/init.d/glassfish > /dev/null <<'EOF'
...
EOF
Why did I use tee instead of cat? The output redirection is not affected by sudo, since the file is opened by the shell before sudo even runs. If you need sudo because you don't otherwise have write permission to /etc/init.d/, you need to run a command like tee that opens the file itself.

Shell script create unexpected file "start" on starting process

I've found and modified a simple shell script to start/stop a jar, but when launching the script it creates an extra empty start file.
I cannot understand why. Any clue?
#!/bin/bash
case $1 in
start)
if [[ -e myprog.pid ]]
then
echo "myprog.pid found. Is myprog already running?"
else
exec java -jar myprog-0.0.1-SNAPSHOT.jar 1>/dev/null 2>$1 &
echo $! > myprog.pid;
fi
;;
stop)
kill $(cat myprog.pid);
rm myprog.pid
;;
*)
echo "usage: myprog {start|stop}" ;;
esac
exit 0
Your problem is 2>$1. That's a typo.
You meant 2>&1.
What you wrote is expanded by the shell as 2>start and creates your file.

Resources