Passing variables (array index) from remote ssh to global function - arrays

BASH
I'm writing a script to check services on a selection of hosts for a selection of users. The selected users are stored in an array. The script connects via ssh to each selected host and would go looping through the array logging in to each selected user. However the script fails to read the index of array given by for cycle during ssh - it takes only the 1st (0) index of the array. I've tried almost every possible quoting, escaping, etc... When i escape the $j variable in the echo or "checking" step, i get a syntax error: operand expected (error token is ā€œ$jā€) error. Any ideas on how to make it work? Is it even possible?
usr1=("user1" "user2")
function tusr {
declare -n tmpp="$1";
echo "${tmpp[$j]}"
}
function testchk {
echo "Logging in as $myuser to $1"
ssh -tq $myuser#$1.bar.com "
for j in ${!usr1[#]}; do
echo \$j
echo ${usr1[$j]}
echo "Checking $(tusr usr1 "\$j"):"
done
"
}
srv="foo"
testchk "$srv"
When echoing the escaped $j, it prints out the correct value.
My output:
0
user1
Checking user1:
1
user1
Checking user1:
Expected output:
0
user1
Checking user1:
1
user2
Checking user2:

The remote shell doesn't know of your local variables or functions, so you'll need to pass their declarations.
ssh -tq $myuser#$1.bar.com "
$(declare -p usr1)
$(declare -f tusr)
for j in \${!usr1[#]}
do
echo \"\$j\"
echo \"\${usr1[\$j]}\"
echo \"Checking \$(tusr usr1 \$j):\"
done
"
But what you're trying to do is a real mix of things and it's confusing. Are you trying to make the remote user to execute the function tusr locally ??

Related

How can I use two arrays to check if DNS records exists in Bash?

I want to check if the following records exist with two arrays. I'm not sure if this is the best way of going about it, but from the logic it looks like it may be possible from the code below:
Domain_checking () {
array=(
grafana
kibana
prometheus
alertmanager
)
array2=(
Name
NXDOMIAN
)
for index in ${!array[*]}; do
echo "checking that ${array[$index]} exists in the domain domain.co.uk"
DOMAIN_CHECK=$(nslookup ${array[$index]}.domain.co.uk | grep {array2[$index]})
if [[ $DOMAIN_CHECK == *'Name'* ]]; then
echo "The A record for ${array[$index]}.domain.co.uk exists"
elif [[ $DOMAIN_CHECK == *'NXDOMIAN'* ]]; then
echo "The A record for ${array[$index]}.domain.co.uk dose not exist"
fi
done
}
Domain_checking
When the code above is run, the loop does start and for the echo statement, I see the values in both arrays when I add {array2[$index]} to the echo statement.
But the array values are not present in DOMAIN_CHECK, which I'm not sure as to why this is as the for loop does iterate.
So I would expect that DOMAIN_CHECK should have some sort of value and hit the if statement but for some reason, this doesn't seem to be the case. Why is that?
It appears you're only using nslookup to see if the domain exists or not, rather than looking for specific information from the command. You can simplify by just checking the exit code instead of using grep:
Domain_checking () {
array=(
grafana
kibana
prometheus
alertmanager
)
for domain in ${array[#]}
do
if nslookup "${domain}.domain.co.uk" >/dev/null 2>&1 ; then
echo "$domain exists"
else
echo "$domain does not exist"
fi
done
}
Domain_checking
If the domain record exists, nslookup will return 0 and the if condition will be satisfied. Anything else indicates a failure and the control will fall through to the else.
You use $index as an index to both arrays, but there's a matching entry in $array2 only for the first entry. That's why the other entries aren't showing up, and also why grep is missing it's required argument.
Thinking through your logic, I don't see any reason not to remove the second array completely and hard code in Name for the grep.
Come think of it, the first array isn't helping much either. You could simplify the code by iterating across the names themselves rather than their array indices.
domain=some.thing
names="kibana prometheus graphite"
for name in $names; do
nslookup $name.$domain ....
done

Bash parameter expansion, indirect reference, and backgrounding

After struggling with this issue for several hours and searching here and failing to come up with a matching solution, it's time to ask:
In bash (4.3) I'm attempting to do a combination of the following:
Create an array
For loop through the values of the array with a command that isn't super fast (curl to a web server to get a value), so we background each loop to parallelize everything to speed it up.
Set the names of the values in the array to variables assigned to values redirected to it from a command via "read"
Background each loop and get their PID into a regular array, and associate each PID with the related array value in an associative array so I have key=value pairs of array value name to PID
Use "wait" to wait for each PID to exit 0 or throw an error telling us which value name(s) in the array failed to exit with 0 by referencing the associative array
I need to be able export all of the VAR names in the original array and their now-associated values (from the curl command results) because I'm sourcing this script from another bash script that will use the resulting exported VARs/values.
The reason I'm using "read" instead of just "export" with "export var=$(command)" or similar, is because when I background and get the PID to use "wait" with in the next for loop, I actually (incorrectly) get the PID of the "export" command which always exits 0, so I don't detect an error. When I use read with the redirect to set the value of the VAR (from name in the array) and background, it actually gets the PID of the command and I catch any errors in the next loop with the "wait" command.
So, basically, this mostly appears to work, except I realized the "read" command doesn't actually appear to be substituting the variable to the array name value properly in a way that the redirected command sends its output to that name in order to set the substituted VAR name to a value. Or, maybe the command is just entirely wrong so I'm not correctly redirecting the result of my command to a VAR name I'm attempting to set.
For what it's worth, when I run the curl | python command by hand (to pull the value and then parse the JSON output) it is definitely succeeding, so I know that's working, I just can't get the redirect to send the resulting output to the VAR name.
Here's a example of what I'm trying to do:
In parent script:
# Source the child script that has the functions I need
source functions.sh
# Create the array
VALUES=(
VALUE_A
VALUE_B
VALUE_C
)
# Call the function sourced from the script above, which will use the above defined array
function_getvalues
In child (sourced) script:
function_getvalues()
{
curl_pids=( )
declare -A value_pids
for value in "${VALUES[#]}"; do
read ${value} < <(curl -f -s -X GET http://path/to/json/value | python3 -c "import sys, json; print(json.load(sys.stdin)['data']['value'])") & curl_pids+=( $! ) value_pids+=([$!]=${value})
done
for pid in "${curl_pids[#]}"; do
wait "$pid" && echo "Successfully retrieved value ${value_pids[$pid]} from Webserver." || { echo "Something went wrong retrieving value ${value_pids[$pid]}, so we couldn't get the output data needed from Webserver. Exiting." ; exit 1 ; }
done
}
The problem is that read, when run in the background, isn't connected to a standard in.[details] Consider this simplified, working example with comment how to cripple it:
VALUES=( VALUE_A VALUE_B )
for value in "${VALUES[#]}"; do
read ${value} < <(echo ${RANDOM}) # add "&" and it stops working
done
echo "VALUE_A=${VALUE_A}"
echo "VALUE_B=${VALUE_B}"
You might be able to do this with coproc, or using read -u with automatic file descriptor allocation, but really this is a job for temporary files:
tmpdir=$(mktemp -d)
VALUES=( VALUE_A VALUE_B )
for value in "${VALUES[#]}"; do
(sleep 1; echo ${RANDOM} > "${tmpdir}"/"${value}") &
done
for value in "${VALUES[#]}"; do
wait_file "${tmpdir}"/"${value}" && {
read -r ${value} < "${tmpdir}"/"${value}";
}
done
echo "VALUE_A=${VALUE_A}"
echo "VALUE_B=${VALUE_B}"
rm -r "${tmpdir}"
This example uses wait_file helper, but you might use inotifywait if you don't mind some dependencies on OS.

Nagios bash script returns no output when executed through check_nrpe

My nagios bash script works fine from the client's command line.
When I execute the same script through check_nrpe from the nagios server it returns the following message "CHECK_NRPE: No output returned from daemon."
Seems like a command in the bash script is not being executed.
arrVars=(`/usr/bin/ipmitool sensor | grep "<System sensor>"`)
#echo "Hello World!!"
myOPString=""
<Process array and determine string to echo along with exit code>
echo $myOPString
if [[ $flag == "False" ]]; then
exit 1
else
exit 0
fi
"Hello World" shows up on the nagios monitoring screen if I uncomment the echo statement.
I am new to linux but seems like the nagios user isn't able to execute ipmitool
arrVars=(`/usr/bin/ipmitool sensor | grep "<System sensor>"`)
Check the output of the above, You can echo it and check for the values. If it still does not work use another script to be called by this to get the output and assign it to a variable
exit 1
This refers to the Severity , So you would have to define different conditions where the severity changes
Add this line to the sudoers
nagios ALL=(root) NOPASSWD: /usr/bin/ipmitool
Then use "sudo /usr/bin/ipmitool" in your script

How to send array values by mail

in linux scripting,
is there a way to use mail function to one time send array values??
function my_mail_function(){
# send array values
mail array_values_here "mymail#domain.tld" ;
}
Thank you
You can step through an array with just a little bit of bash code.
#!/bin/bash
# Here's a simple array...
a=(one two three)
# The brackets encapsulate multiple commands to feed to the stdin of sendmail
(
echo "To: Mister Target <target#example.com>"
echo "From: Julio Fong <jf#example.net>"
echo "Subject: Important message!"
echo ""
count=1
for item in ${a[#]}; do
printf "Value %d is %s\n" "$count" "$item"
((count++))
done
echo ""
) | /usr/sbin/sendmail -oi -fjf#example.net target#example.com
Note that it'll be safer to use sendmail directly rather than relying on the availability and configuration of a mail or Mail command. Your sendmail binary may not be in the same place as mine; if /usr/sbin/ doesn't work for you, check /usr/libexec/. It'll depend on the distribution of Linux you're running.
The proper way to use mail is:
mail -s "subject here" recipient1 recipient2 ...
the command reads the email body from stdin so you can format it how you like and read it in from a pipe or a here-doc or a file or ...
function my_mail_function(){
printf "%s\n" "${array_var[#]}" | mail -s "array values" mymail#domain.tld
}

Check database connectivity using Shell script

I am trying to write a shell script to check database connectivity. Within my script I am using the command
sqlplus uid/pwd#database-schemaname
to connect to my Oracle database.
Now I want to save the output generated by this command (before it drops to SQL prompt) in a temp file and then grep / find the string "Connected to" from that file to see if the connectivity is fine or not.
Can anyone please help me to catch the output and get out of that prompt and test whether connectivity is fine?
Use a script like this:
#!/bin/sh
echo "exit" | sqlplus -L uid/pwd#dbname | grep Connected > /dev/null
if [ $? -eq 0 ]
then
echo "OK"
else
echo "NOT OK"
fi
echo "exit" assures that your program exits immediately (this gets piped to sqlplus).
-L assures that sqlplus won't ask for password if credentials are not ok (which would make it get stuck as well).
(> /dev/null just hides output from grep, which we don't need because the results are accessed via $? in this case)
You can avoid the SQL prompt by doing:
sqlplus uid/pwd#database-schemaname < /dev/null
SqlPlus exits immediately.
Now just grep the output of the above as:
if sqlplus uid/pwd#database-schemaname < /dev/null | grep 'Connected to'; then
# have connectivity to Oracle
else
# No connectivity
fi
#! /bin/sh
if echo "exit;" | sqlplus UID/PWD#database-schemaname 2>&1 | grep -q "Connected to"
then echo connected OK
else echo connection FAIL
fi
Not knowing whether the "Connected to" message is put to standard output or standard error, this checks both. "qrep -q" instead of "grep... >/dev/null" assumes Linux.
#!/bin/bash
output=`sqlplus -s "user/pass#POLIGON.TEST " <<EOF
set heading off feedback off verify off
select distinct machine from v\\$session;
exit
EOF
`
echo $output
if [[ $output =~ ERROR ]]; then
echo "ERROR"
else
echo "OK"
fi
Here's a good option which does not expose the password on the command line
#!/bin/bash
CONNECT_STRING=<USERNAME>/<PASS>#<SID>
sqlplus -s -L /NOLOG <<EOF
whenever sqlerror exit 1
whenever oserror exit 1
CONNECT $CONNECT_STRING
exit
EOF
SQLPLUS_RC=$?
echo "RC=$SQLPLUS_RC"
[ $SQLPLUS_RC -eq 0 ] && echo "Connected successfully"
[ $SQLPLUS_RC -ne 0 ] && echo "Failed to connect"
exit SQLPLUS_RC
none of the proposed solutions works for me, as my script is executed in machines running several countries, with different locales, I can't simply check for one String simply because this string in the other machine is translated to a different language. As a solution I'm using SQLcl
https://www.oracle.com/database/technologies/appdev/sqlcl.html
which is compatible with all sql*plus scripts and allow you to test the database connectivity like this:
echo "disconnect" | sql -L $DB_CONNECTION_STRING > /dev/null || fail "cannot check connectivity with the database, check your settings"
#!/bin/sh
echo "exit" | sqlplus -S -L uid/pwd#dbname
if [ $? -eq 0 ]
then
echo "OK"
else
echo "NOT OK"
fi
For connection validation -S would be sufficient.
The "silent" mode doesn't prevent terminal output. All it does is:
-S Sets silent mode which suppresses the display of
the SQL*Plus banner, prompts, and echoing of
commands.
If you want to suppress all terminal output, then you'll need to do something like:
sqlplus ... > /dev/null 2>&1
This was my one-liner for docker container to wait until DB is ready:
until sqlplus -s sys/Oracle18#oracledbxe/XE as sysdba <<< "SELECT 13376411 FROM DUAL; exit;" | grep "13376411"; do echo "Could not connect to oracle... sleep for a while"; sleep 3; done
And the same in multiple lines:
until sqlplus -s sys/Oracle18#oracledbxe/XE as sysdba <<< "SELECT 13376411 FROM DUAL; exit;" | grep "13376411";
do
echo "Could not connect to oracle... sleep for a while";
sleep 3;
done
So it basically does select with magic number and checks that correct number was actually returned.

Resources