Multi-line ssh where results need to be saved - bash

I have a script where I have the following code:
results=$(ssh $server 'keys=$(redis-cli scan 0) ; keypairs="" ; for key in $keys ; do value=$(redis-cli get $key) ; keypairs="$keypairs$key $value\n" ; done ; echo $keypairs')
This code works, but it is rather ugly to look at so I am trying to perform this on multiple lines and I can't quite figure out how it should be done.
I have tried
results=$(ssh $server "
keys=$(redis-cli scan 0)
keypairs=\"\"
for key in $keys
do
value=$(redis-cli get $key)
keypairs=\"$keypairs$key $value\n\"
done
echo $keypairs
")
But this returns the error Could not connect to Redis at 127.0.0.1:6379: Connection refused. The weird thing is that the error shows up before it even has asked for the credentials for ssh:
Could not connect to Redis at 127.0.0.1:6379: Connection refused
Could not connect to Redis at 127.0.0.1:6379: Connection refused
host#server's password:
Can anyone tell me what is going on here and how I should go about having this section be a multi-line?

A multiline command can be used to assign to a variable, such as:
multilinecommand=$(test=check
if [ $test == "check" ]
then
echo "Yes"
else
echo "No"
fi
)
where echo $multilinecommand shows Yes.
If you however want to split a single command into multiple lines, you have to add a backslash at the end of the line to denote that the command isn't finished yet. Your command therefore becomes:
results=$(ssh $server " \
keys=$(redis-cli scan 0) \
keypairs=\"\"
for key in $keys
do
value=$(redis-cli get $key)
keypairs=\"$keypairs$key $value\n\"
done
echo $keypairs
")
because keys and keypairs are needed for the ssh command

Related

How do I loop through elements in two bash arrays at the same time

I have a bash script that should execute mongodump on multiple databases within a single mongo instance.
The script should dynamically inject the Mongo-URIs and backup filenames as below :
mongodump --uri=$endpoint --authenticationDatabase admin --gzip --archive=/tmp/$filename.gz
When the script runs its facing an error - it appears its not injecting the variables correctly :
dump.sh
Starting-BACKUP
dump.sh
platforms-2022-10-25-19:05:20
sports-2022-10-25-19:05:20
mongodb://root:mypassword#mongodb-prom/platforms
mongodb://root:mypassword#mongodb-prom/sports
2022-10-25T19:05:20.392+0000 Failed: error creating intents to dump: error getting collections for database `platformsmongodb://root:mypassword#mongodb-prom/sports`: (InvalidNamespace) Invalid database name: 'platformsmongodb://root:mypassword#mongodb-prom/sports'
2022-10-25T19:05:50.409+0000 Failed: can't create session: could not connect to server: server selection error: server selection timeout, current topology: { Type: Single, Servers: [{ Addr: localhost:27017, Type: Unknown, Last error: connection() error occurred during connection handshake: dial tcp [::1]:27017: connect: connection refused }, ] }
Database backup was successful
Both the Mongo-URIs and backup filenames are being populated from arrays as below :
#declare -a BACKUP_NAMES=()
#declare -a BACKUP_ENDPOINTS=()
#declare –a DATABASES=()
#declare –a RAW_DBNAMES=()
#declare –a DATABASES=()
DATABASES+=("platforms")
DATABASES+=("sports")
BACKUP_RETURN_CODE=${?}
MONGODB_URI="mongodb://root:mypassword#mongodb-prom/"
NOW="$(date +"%F")-$(date +"%T")"
#array for raw db names
for DATABASE in "${DATABASES[#]}";
do
RAW_DBNAMES+=("$DATABASE")
done
#array for constructing backup filenames
for DATABASE in "${DATABASES[#]}";
do
#echo $DATABASE
BACKUP_NAMES+=("$DATABASE")
done
#construct backup filenames
cnt=${#BACKUP_NAMES[#]}
for ((i=0;i<cnt;i++)); do
BACKUP_NAMES[i]="${BACKUP_NAMES[i]}-$NOW"
echo "${BACKUP_NAMES[i]}"
done
#construct db endpoints
for ((i=0;i<cnt;i++)); do
uri="$MONGODB_URI${RAW_DBNAMES[i]}"
echo "$uri"
BACKUP_ENDPOINTS+=$uri
done
#pass BACKUP_ENDPOINTS & BACKUP_FILENAMES to mongodump
for ((i=0; i<${#BACKUP_NAMES[#]}; i++));
do
mongodump --uri="${BACKUP_ENDPOINTS[$i]}" --authenticationDatabase admin --gzip --archive=/tmp/"${BACKUP_NAMES[$i]}".gz
done
#If the return code is non-zero then the backup was not successful
if [[ 0 != ${BACKUP_RETURN_CODE} ]]
then
echo "Database backup has failed"
exit ${BACKUP_RETURN_CODE}
else
echo "Database backup was successful"
fi
exit 0
As a check I used these two sample arrays and I have verified that I can indeed loop through two arrays at the same time although this particular example is concatenating the elements (Is this how it works in general?):
array1=( 1 2 3 )
array2=("toronto""new york")
for ((i=0; i<${#array1[#]}; i++));
do echo "${array1[$i]}${array2[$i]}";
done
but I am not sure why in my case the strings are concatenating so wrongly.
What am I missing ?
You don't need multiple arrays create just one and rearrange it like this:
arr=(
# id item product
1 apple juice
2 banana smuzi
3 'raw potato' chips
# ...
)
N=${#arr[*]} # number of items in array
C=3 # number of 'columns'
Then loop over it like so:
for ((i=0; i<$N; i+=$C)); {
read id item product <<< "${arr[#]:$i:$C}"
echo "id: $id"
echo "item: $item"
echo "product: $product"
echo '-----------------'
}
Result:
id: 1
item: apple
product: juice
-----------------
id: 2
item: banana
product: smuzi
-----------------
id: 3
item: raw
product: potato chips
-----------------
p.s. check out this backup script for example it's for psql but probably could be useful.
Yes, it turned i was instantiating the BACKUP_ENDPOINTS array the wrong way.
This is the correct way :
#construct db endpoints
for ((i=0;i<cnt;i++)); do
# uri="$MONGODB_URI${RAW_DBNAMES[i]}"
# echo "$uri"
# BACKUP_ENDPOINTS+=$uri THIS IS WRONG !!!
BACKUP_ENDPOINTS[i]="$MONGODB_URI${RAW_DBNAMES[i]}"
done
for ENDPOINT in "${BACKUP_ENDPOINTS[#]}"; do
echo "$ENDPOINT"
done
#pass BACKUP_ENDPOINTS & BACKUP_FILENAMES to mongodump
for ((i=0; i<${#BACKUP_NAMES[#]}; i++));
do
mongodump --uri="${BACKUP_ENDPOINTS[$i]}" --authenticationDatabase admin --gzip --archive=/tmp/"${BACKUP_NAMES[$i]}".gz
done

How start ejabberdctl from bash script properly?

I need to register many thousands of users in ejabberd from csv file. For this, I wrote a simple script.
#!/bin/sh
OLDIFS=$IFS
IFS=','
[ ! -f $INPUT ] && { echo "$INPUT file not found"; exit 99; }
while read username domain pass p1 p2 p3 p4
do
echo "ejabberdctl register $username $domain $pass"
ejabberdctl register $username $domain $pass
done < users.csv
IFS=$OLDIFS
But in the end, the answer is: Error: cannot_register
If I just run the line copied from the output, everything is ok. The user is created normally.
This is just a little trick for later: once you get your loop working, if you consider ejabberdctl is too slow, you can try using the ReST API. That should be a lot faster when doing many requests.
Configure temporarily something like this (remember to remove this when you finished):
listen:
-
port: 5280
module: ejabberd_http
tls: false
request_handlers:
/api: mod_http_api
api_permissions:
"console commands":
from:
- ejabberd_ctl
- mod_http_api
who: all
what: "*"
modules:
mod_http_api: {}
Then execute this in a shell to register an account:
curl 'localhost:5280/api/register?user=user2&host=localhost&password=somepass123'

Getting an error for unexpected else in bash

[SOLVED]
I'm pretty new tho bash-/shell-scripting and trying setup a check for ip address on a server which gets about once a week a new ip.
The script will then send the new ip to the users.
My problem is, that I'm getting a syntax-error in the last if-else statement for "unexpected" else and can wrap my head around why.
My first iteration didn't use functions, but instead one multi lined if-else which got me the same error. The functions on their own seem to work just fine.
#!/bin/bash
# script to send the new server ip to the users
# get the recent ip address of the system
new_ip=$(ip route get 8.8.8.8 | awk -F"src " 'NR==1{split($2,a," ");print a[1]}')
file=old_ip.txt
function ip_mail(){
source $file
if [ $new_ip != $old_ip ]
then
# email-address changed for obvious reasons
mail -s "New Server IP" [hidden]#[hidden].com <<< "$new_ip"
echo "old_ip=$new_ip" > old_ip.txt
exit 0
fi
exit 0
}
function set_old(){
touch old_ip.txt
echo "old_ip=$new_ip" > old_ip.txt
exit 0
}
if [ $file ]
then
ip_mail()
else
set_old()
fi

Not able to connect to socket using socat

I am trying to parse rsyslog logs. For this i am sending all my logs to socat which is then sending them to Unix Domain Socket. That socket is created via perl script which is listening on that socket to parse logs.
My bash script to which rsyslog is sending all log is
if [ ! `pidof -x log_parser.pl` ]
then
./log_parser.pl & 1>&1
fi
if [ -S /tmp/sock ]
then
/usr/bin/socat -t0 -T0 - UNIX-CONNECT:/tmp/sock 2>> /var/log/socat.log
fi
/tmp/sock is created using perl script log_parser.pl which is
use IO::Socket::UNIX;
sub socket_create {
$socket_path = '/tmp/sock';
unlink($socket_path);
$listner = IO::Socket::UNIX->new(
Type => SOCK_STREAM,
Local => $socket_path,
Listen => SOMAXCONN,
Blocking => 0,
)
or die("Can't create server socket: $!\n");
$socket = $listner->accept()
or die("Can't accept connection: $!\n");
}
socket_create();
while(1) {
chomp($line=<$socket>);
print "$line\n";
}
There is this error i am getting from socat which is
2015/02/24 11:58:01 socat[4608] E connect(3, AF=1 "/tmp/sock", 11): Connection refused
I am no champion in sockets so i am not able to understand what is this. Please help. Thanks in advance.
The main issue is that when i kill my perl script then bash script is suppose to call it again and start it.
What actually happening is that sript is started but socat is not started instead it give this error and never start.
I can duplicate your error if I don't run your perl program before trying to use socat. Here is what works for me:
1) my_prog.pl:
use strict;
use warnings;
use 5.016;
use Data::Dumper;
use IO::Socket::UNIX;
my $socket_path = '/tmp/sock';
unlink $socket_path;
my $socket = IO::Socket::UNIX->new(
Local => $socket_path,
Type => SOCK_STREAM,
Listen => SOMAXCONN,
) or die "Couldn't create socket: $!";
say "Connected to $socket_path...";
my $CONN = $socket->accept()
or die "Whoops! Failed to open a connection: $!";
{
local $/ = undef; #local -> restore previous value when the enclosing scope, delimited by the braces, is exited.
#Setting $/ to undef puts file reads in 'slurp mode' => whole file is considered one line.
my $file = <$CONN>; #Read one line.
print $file;
}`
2) $ perl my_prog.pl
3) socat -u -v GOPEN:./data.txt UNIX-CONNECT:/tmp/sock
The -u and -v options aren't necessary:
-u Uses unidirectional mode. The first address is only used for
reading, and the second address is only used for writing (exam-
ple).
-v Writes the transferred data not only to their target streams,
but also to stderr. The output format is text with some conver-
sions for readability, and prefixed with "> " or "< " indicating
flow directions.
4) You can also do it like this:
cat data.txt | socat STDIN UNIX-CONNECT:/tmp/sock
Pipe stdout of cat command to socat, then list STDIN as one of socat's files.
Response to comment:
This bash script works for me:
#!/usr/bin/env bash
echo 'bash script'
../pperl_programs/my_prog.pl &
sleep 1s
socat GOPEN:./data.txt UNIX-CONNECT:/tmp/sock
It looks like the perl script doesn't have enough time to setup the socket before socat tries to transfer data.

Execute psql query in bash

I have a problem with executing a psql-query in a bash script.
Below is my code.
run_sql(){
sql_sel="$1;";#sql select
table=$2;#table name
for i in "${!GP_SERVER_NAMES[#]}"
do
logmsg "Executing [$sql_sel] on "${GP_SERVER_NAMES[$i]}": " $loglvl;
result_host[$i]=`${PSQL_HOST[$i]}${sql_sel}`;
#result_host[$i]=cleanresult "`$(${PSQL_HOST[$i]} "$tx_fix $sql_sel" 2>&1`");
if [[ `checkresult "${result_host[$i]}"` != 'true' ]]; then
logmsg "Error occured during sql select: Result for "${GP_SERVER_NAMES[$i]}" '${table}': ${result_host[$i]};" '1' '1';
raise_alarm "${GP_SYNC_SQL_ERR}" "${i}" "${table}" "${result_host}";
fi
logmsg "Result for" ${GP_SERVER_NAMES[$i]} " '${table}': ${result_host[$i]}";
done
final_result='true';
for i in "${!result_host[#]}"
do
if [[ `checkresult "${result_host[$i]}"` = 'true' ]]; then
final_result='false';
I am trying to executing the query on many different servers, with the following command
result_host[$i]=${PSQL_HOST[$i]}${sql_sel};
The above variables have the following meaning:
1. result_host[$i] : is an array that holds the i-th result of the sql query.
2. PSQL_HOST[$i] : is the command line psql-statement, including the IP address which is of the form
psql -t -q -h -U password -d database -c
3. $sql_sel : is the sql_statement
When I run the script, I get the following output.
scriptname.sh: line 168: SELECT: command not found
ERROR: syntax error at end of input
LINE 1: SELECT
^
Why is that? Any help, comments or suggestions would be appreciated.

Resources