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

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

Related

the bash script only reboot the router without echoing whether it is up or down

#!/bin/bash
ip route add 10.105.8.100 via 192.168.1.100
date
cat /home/xxx/Documents/list.txt | while read output
do
ping="ping -c 3 -w 3 -q 'output'"
if $ping | grep -E "min/avg/max/mdev" > /dev/null; then
echo 'connection is ok'
else
echo "router $output is down"
then
cat /home/xxx/Documents/roots.txt | while read outputs
do
cd /home/xxx/Documents/routers
php rebootRouter.php "outputs" admin admin
done
fi
done
The other documents are:
lists.txt
10.105.8.100
roots.txt
192.168.1.100
when i run the script, the result is a reboot of the router am trying to ping. It doesn't ping.
Is there a problem with the bash script.??
If your files only contain a single line, there's no need for the while-loop, just use read:
read -r router_addr < /home/xxx/Documents/list.txt
# the grep is unnecessary, the return-code of the ping will be non-zero if the host is down
if ping -c 3 -w 3 -q "$router_addr" &> /dev/null; then
echo "connection to $router_addr is ok"
else
echo "router $router_addr is down"
read -r outputs < /home/xxx/Documents/roots.txt
cd /home/xxx/Documents/routers
php rebootRouter.php "$outputs" admin admin
fi
If your files contain multiple lines, you should redirect the file from the right-side of the while-loop:
while read -r output; do
...
done < /foo/bar/baz
Also make sure your files contain a newline at the end, or use the following pattern in your while-loops:
while read -r output || [[ -n $output ]]; do
...
done < /foo/bar/baz
where || [[ -n $output ]] is true even if the file doesn't end in a newline.
Note that the way you're checking for your routers status is somewhat brittle as even a single missed ping will force it to reboot (for example the checking computer returns from a sleep-state just as the script is running, the ping fails as the network is still down but the admin script succeeds as the network just comes up at that time).

not able to use ssh within a shell script

I have this shell script which ssh to other server, find few specific files(.seq files older than 50 mnts) and writes their name to another file.
#! /usr/bin/bash
while read line
do
#echo $line
if [[ $line =~ ^# ]];
then
#echo $line;
continue;
else
serverIP=`echo $line|cut -d',' -f1`
userID=`echo $line|cut -d',' -f2`
fi
done < sftp.conf
sshpass -p red32hat ssh $userID#$serverIP
cd ./perl
for files in `find -name "*.seq" -mmin +50`
do
#sshpass -p red32hat scp *.seq root#rinacac-test:/root/perl
echo $files>>abcde.txt
done
exit;
#EOF
Now problem is that when I run it.. neither it writes to abcde.txt file nor it is exiting from the remote server. when I manually execute the exit command...it exists saying "perl no such file or directory"... while I have perl sub directory in my home directory..
other thing is when I run the for loop portion of the script on the 2nd server(by directly logging into it) it is working fine and writing to abcde.txt filr...please help...
ssh takes commands either on standard input or as the last parameter. You can therefore do this (very dynamic but tricky to get the expansions right):
ssh user#host <<EOF
some
custom
commands
EOF
or this (less dynamic but can take simple parameters without escaping):
scp my_script.sh user#host:
ssh user#host './my_script.sh'

Shell script calling ssh: how to interpret wildcard on remote server

I work on a certain customer environment on a daily basis, comprised of 5 AIX servers, and sometimes I need to issue a same command on all 5 of them.
So I set up SSH key-based authentication between the servers, and whipped up a little ksh script that broadcasts the command to all of them:
#!/usr/bin/ksh
if [[ $# -eq 0 ]]; then
print "broadcast.ksh - broadcasts a command to the 5 XXXXXXXX environments and returns output for each"
print "usage: ./broadcast.ksh command_to_issue"
exit
fi
set -A CUST_HOSTS aaa bbb ccc ddd eee
for host in ${CUST_HOSTS[#]}; do
echo "============ $host ================"
if [[ `uname -n` = $host ]]; then
$*
continue
fi
ssh $host $*
done
echo "========================================="
echo "Finished"
Now, this works just fine, until I want to use a wildcard on the remote end, something like:
./broadcast.ksh ls -l java*
since the '*' is expanded on the local system as opposed to the remote.
Now, if using ssh remote commands, I can get around this by using single quotes:
ssh user#host ls -l java* <-- will _not_ work as expected, since asterisk will be interpreted locally
ssh user#host 'ls -l java*' <-- _will_ work as expected, since asterisk will be interpreted on the remote end
Now, I have tried to incorporate that into my script, and have tried to create a $command variable made up of the $* contents surrounded by single quotes, but have drowned in a sea of escaping backslashes and concatenation attempts in ksh, to no avail.
I'm sure there's a simple solution to this, but I'm not finding it so thought I would come out and ask.
Thanks,
James
As you found, passing an asterisk as an argument to your script doesn't work because the shell expands it before the arguments are processed. Try double-quoting $* and either escaping asterisks/semi-colons etc with backslashes in your script call, or single quoting the command.
for host in ${CUST_HOSTS[#]}; do
echo "============ $host ================"
if [[ `uname -n` = $host ]]; then
"$*"
continue
fi
ssh $host "$*"
done
$ ./broadcast.ksh ls -l java\*
$ ./broadcast.ksh 'ls -l java*; ls -l *log'
I wanted to comment but still too low on the totum, but Josh's single quote suggestion should work.
I spun up a couple of vms each with 2 files in /tmp : /tmp/foo1 and /tmp/foo2
then used a variation of your script
root#jdsdrop1:~# cat foo.sh
#!/usr/bin/ksh
if [[ $# -eq 0 ]]; then
print "broadcast.ksh - broadcasts a command to the 5 XXXXXXXX environments and returns output for each"
print "usage: ./broadcast.ksh command_to_issue"
exit
fi
set -A CUST_HOSTS jdsdropfed1 jdsdropfed2-2
for host in ${CUST_HOSTS[#]}; do
echo "============ $host ================"
if [[ `uname -n` = $host ]]; then
$*
continue
fi
ssh $host $*
done
echo "========================================="
echo "Finished"
root#jdsdrop1:~# ./foo.sh 'ls /tmp/foo*'
============ jdsdropfed1 ================
/tmp/foo1
/tmp/foo2
============ jdsdropfed2-2 ================
/tmp/foo1
/tmp/foo2
=========================================
Finished
root#jdsdrop1:~# ssh jdsdropfed1 "ls /tmp/foo*"
/tmp/foo1
/tmp/foo2
root#jdsdrop1:~# ssh jdsdropfed2-2. "ls /tmp/foo*"
/tmp/foo1
/tmp/foo2

Pseudo-terminal will not be allocated because stdin is not a terminal ssh bash

okay heres part of my code when I ssh to my servers from my server.txt list.
while read server <&3; do #read server names into the while loop
serverName=$(uname -n)
if [[ ! $server =~ [^[:space:]] ]] ; then #empty line exception
continue
fi
echo server on list = "$server"
echo server signed on = "$serverName"
if [ $serverName == $server ] ; then #makes sure a server doesnt try to ssh to itself
continue
fi
echo "Connecting to - $server"
ssh "$server" #SSH login
echo Connected to "$serverName"
exec < filelist.txt
while read updatedfile oldfile; do
# echo updatedfile = $updatedfile #use for troubleshooting
# echo oldfile = $oldfile #use for troubleshooting
if [[ ! $updatedfile =~ [^[:space:]] ]] ; then #empty line exception
continue # empty line exception
fi
if [[ ! $oldfile =~ [^[:space:]] ]] ; then #empty line exception
continue # empty line exception
fi
echo Comparing $updatedfile with $oldfile
if diff "$updatedfile" "$oldfile" >/dev/null ; then
echo The files compared are the same. No changes were made.
else
echo The files compared are different.
cp -f -v $oldfile /infanass/dev/admin/backup/`uname -n`_${oldfile##*/}_$(date +%F-%T)
cp -f -v $updatedfile $oldfile
fi
done
done 3</infanass/dev/admin/servers.txt
I keep on getting this error and the ssh doesn't actually connect and perform the code on the server its suppose to be ssh'd on.
Pseudo-terminal will not be allocated because stdin is not a terminal
I feel like everything the guy above just said is so wrong.
Expect?
It's simple:
ssh -i ~/.ssh/bobskey bob#10.10.10.10 << EOF
echo I am creating a file called Apples in the /tmp folder
touch /tmp/apples
exit
EOF
Everything in between the 2 "EOF"s will be run in the remote server.
The tags need to be the same. If you decide to replace "EOF" with "WayneGretzky", you must change the 2nd EOF also.
You seem to assume that when you run ssh to connect to a server, the rest of the commands in the file are passed to the remote shell running in ssh. They are not; instead they will be processed by the local shell once ssh terminates and returns control to it.
To run remote commands through ssh there are a couple of things you can do:
Write the commands you want to execute to a file. Copy the file to the remote server using scp, and execute it with ssh user#remote command
Learn a bit of TCL and use expect
Write the commands in a heredoc, but be careful with variable substitution: substitution happens in the client, not on the server. For example this will output your local home directory, not the remote:
ssh remote <<EOF
echo $HOME
EOF
To make it print the remote home directory you have to use echo \$HOME.
Also, remember that data files such as filelist.txt have to be explicitly copied if you want to read them on the remote side.

Check if a program exists in bash

I am trying to check if md5sum or digest exists on solaris and script is used on different machines.
Here is the function in sh script which is called from a ksh script
getMD5cmd ()
{
PATH="${PATH}:/bin:/usr/bin:/usr/sfw/bin:/usr/local/bin:/usr/sbin/bin"
if type -p md5sum;then
MD5CMD=`type -p md5sum`
elif type -p digest;then
MD5CMD="`type -p digest` -a md5"
fi
echo "HERE ${MD5CMD}"
}
When I run scripts I get
-p not found
md5sum not found
-p not found
digest is /bin/digest
HERE
However, when I type it in a terminal, works as exptected
Any Ideas?
Thanks
You are likely running ksh or possibly Bash for your interactive shell. Both of these have a -p option for type. The shell (probably sh) that your script is running in has type but doesn't have the -p option so it's looking for "-p" as the name of an executable and it doesn't find it.
So you could change your script to use ksh or you could use the which program. The latter is probably more portable, since some systems don't have ksh.
As you are setting the PATH, knowing where precisely the command is seems unnecessary.
getMD5cmd ()
{
PATH=${PATH}:/bin:/usr/bin:/usr/sfw/bin:/usr/local/bin:/usr/sbin/bin
md5sum /dev/null >/dev/null 2>&1 && MD5CMD=md5sum || MD5CMD="digest -a md5"
echo "HERE ${MD5CMD}"
}
getMD5cmd
Have you tried the following syntax:
MD5CMD="$(type -p md5sum digest |sed -e 's/digest$/digest -a md5/' |head -1)"
if [ -z "$MD5CMD" ]; then
echo 'no md5 sum command found' >&2
exit 1
fi
echo "HERE $MD5CMD"
I tried this in Cygwin and type will return multiple rows, so it works.
if which md5sum >/dev/null 2>&1; then
md5cmd="md5sum"
elif which digest >/dev/null 2>&1; then
md5cmd="digest -a md5"
else
echo "No md5 command found" >&2
exit 1
fi
$md5cmd YOUR_FILE

Resources