Passing IFS values to a dynamic array - shell

i have a Oracle output (select output) as cand1 cand2 cand3 cand62
now i need to store these values in an array through shell script.
#!/bin/bash
instant_client="/root/ora_client/instantclient_11_2"
output=`$instant_client/sqlplus -s HRUSER/HRUSER#TOMLWF <<EOF
set heading off
set feedback off
set lines 10000
set pagesize 10000
select count (1) from onboardingcandidates o, candidatedetails c where o.candidateid=c.candidateid and o.JOININGSTATUS='0091' and to_date(o.joiningdate)=to_date(sysdate+5);
EOF
exit
query=`$instant_client/sqlplus -s HRUSER/HRUSER#TOMLWF <<EOF
set heading off
set feedback off
set lines 10000
set pagesize 10000
select o.candidateid from onboardingcandidates o, candidatedetails c where o.candidateid=c.candidateid and o.JOININGSTATUS='0091' and to_date(o.joiningdate)=to_date(sysdate+5);
EOF
exit
i=0
echo "Throwing individual arrays:"
while [ $i -le $output ]
do
a=${query[$i]}
echo Candidate[$i]=$a
i=$(($i+1))
done
OUTPUT IS
Throwing individual arrays:
Candidate[0]= cand1 cand2 cand3 cand62
Candidate[1]=
Candidate[2]=
Candidate[3]=
Candidate[4]=
REQUIRED OUTPUT
Throwing individual arrays:
Candidate[0]= cand1
Candidate[1]= cand2
Candidate[2]= cand3
Candidate[3]= cand62

The problem is that query is a string, not an array, so ${query[$i]} does not work as you expect.
In general, to convert a string to an array you would do this:
$ string="foo bar"
$ array=($string)
$ echo ${array[0]}
foo
$ echo ${array[1]}
bar
In your case, if you surround the sqlplus command with parentheses, the output will be stored in an array. Like this:
query=(`$instant_client/sqlplus -s HRUSER/HRUSER#TOMLWF <<EOF
set heading off
set feedback off
set lines 10000
set pagesize 10000
select o.candidateid from onboardingcandidates o, candidatedetails c where o.candidateid=c.candidateid and o.JOININGSTATUS='0091' and to_date(o.joiningdate)=to_date(sysdate+5);
EOF)
And then you can access elements in the query array using an index: ${query[$i]}

Something like this:
$ query="cand1 cand2 cand3 cand62"
$ read -a Candidate <<<$query
$ echo ${Candidate[0]}
cand1
$ echo ${Candidate[1]}
cand2
When you are out of your sqlplus, your "query" variable contains something like above. By simply reading the entire thing into an array as shown above, you can access the values.

Related

BASH assign value in an ARRAY dynamically

I want to assign the value of an array dynamically, and when I change the variable the array changes automatically.
var=5
arr=( $\[$var-1\] $var $\[$var+1\] )
echo ${arr\[#\]}
var=10
echo ${arr\[#\]}
result
4 5 6
4 5 6
I wanted
4 5 6
9 10 11
Try something like this:
v1=abc
v2=efd
v3=ghj
arr=( v1 v2 v3 )
for i in ${arr[#]}; { echo ${!i}; }
abc
efd
ghj
Now lets change values of those vars:
v1=123
v2=456
v3=789
$ for i in ${arr[#]}; { echo ${!i}; }
123
456
789
Elaborating Ivan's trick and applying get/set "method"-style functions -
[P2759474#sdp-bastion ~]$ cat tst
#! /bin/bash
arr=( v1 v2 v3 )
v1(){ (($1))&& var=$(($1+1)); echo $((var-1)); }
v2(){ (($1))&& var=$1; echo $var; }
v3(){ (($1))&& var=$(($1-1)); echo $((var+1)); }
var=5
for i in ${arr[#]}; { printf "%s=" $i; $i; }
v1 14
for i in ${arr[#]}; { printf "%s=" $i; $i; }
[P2759474#sdp-bastion ~]$ ./tst
v1=4
v2=5
v3=6
14
v1=14
v2=15
v3=16
While such automatic updates are perfectly common in spreadsheet applications, Bash doesn’t work that way. If you want to recalculate a few values based on formulas, you have to explicitly tell Bash to do so. The example below generates the outputs that you expect and defines a function called recalculate that you need to call after changes to the formulas or the formulas’ input variables. The rest of the trick is based around how integer evaluation works in Bash.
recalculate() {
local -n source="$1" target="$2"
target=("${source[#]}")
}
formulas=('var - 1' 'var' 'var + 1')
declare -ai arr
var=5
recalculate formulas arr
echo "${arr[#]}" # 4 5 6
var=10
recalculate formulas arr
echo "${arr[#]}" # 9 10 11
(It would be awesome if Bash had an additional pseudo-signal for the trap command, say assignment, which could work like trap 'echo "variable ${1} set to ${!1}"' assignment, but AFAIK, there is no such functionality (plus no separate argument handling in trap); hence the strikethrough. Without that kind of functionality, a function like recalculate might be the closest you can get to the updates you asked for.)
A slightly more elaborate version of recalculate could also (1) handle sparse arrays of formulas correctly, i.e. guarantee to store results under the same indices under which the corresponding formulas were found, and (2) introduce a “reserved” variable name, say index, which can occur in the formulas with an obvious meaning. Just for fun, “because we can”:
recalculate() {
local -n source="$1" target="$2"
local -i index
target=()
for index in "${!source[#]}"; do
target[index]="${source[index]}"
done
}
formulas[1]='index * var - 1'
formulas[3]='index * var'
formulas[5]='index * var + 1'
declare -ai arr
var=5
recalculate formulas arr
echo "${arr[#]#A}" # declare -ai arr=([1]="4" [3]="15" [5]="26")
var=10
recalculate formulas arr
echo "${arr[#]#A}" # declare -ai arr=([1]="9" [3]="30" [5]="51")

Split variable having comma seprated value into two random part in bash shell

I have string="1,2,3,4,5,6,7,8" I want to split string in 2 different variable with unique values as below
string_p1="1,2,3,4,5" string_p2="6,7,8"
Here i dont want any specific defined logic while splitting variable any random splitting is okay.
but i need to ensure that i am not missing any number present in variable string
Please suggest bash script to get the above results ?
One idea using an array and some basic bash string manipulation:
string='1,2,3,4,5,6,7,8'
IFS=, arr=( ${string} ) # break on comma delimiter, store in array
Result:
$ typeset -p arr
declare -a arr=([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="7" [7]="8")
$ len="${#arr[#]}"
8
To generate a random number between 0 and 8 (length of arr[]), eg:
$ n=$(( RANDOM % len ))
$ echo $n
2 # obviously this will vary between 0 and 8 based on value of $RANDOM
Build our final strings:
$ string_p1=${arr[#]:0:n}
$ string_p2=${arr[#]:n}
$ typeset -p string_p1 string_p2
declare -- string_p1="1 2"
declare -- string_p2="3 4 5 6 7 8"
Now replace spaces with commas:
$ string_p1="${string_p1// /,}"
$ string_p2="${string_p2// /,}"
$ typeset -p string_p1 string_p2
declare -- string_p1="1,2"
declare -- string_p2="3,4,5,6,7,8"
NOTE: because the split is based solely on $RANDOM there is a good chance one of the resulting strings can be empty (eg, n=0); OP can add more logic to address this issue as needed (eg, if n=0 then set n=1 to ensure string_p1 is not empty)
Taking for a spin with a different input:
string='3,24,666.83,2,9,0,34,23,45,12,1'
IFS=, arr=( ${string} )
len="${#arr[#]}"
n=$(( RANDOM % len ))
string_p1=${arr[#]:0:n}
string_p2=${arr[#]:n}
string_p1="${string_p1// /,}"
string_p2="${string_p2// /,}"
typeset -p len n string_p1 string_p2
This generates:
declare -- len="11"
declare -- n="8"
declare -- string_p1="3,24,666.83,2,9,0,34,23"
declare -- string_p2="45,12,1"
I tried with this:
string="1,2,3,4,5,6,7,8"
echo ${string:0:${#string}/2}
echo ${string:${#string}/2}
and it splits the string in half, this is the output:
1,2,3,4
,5,6,7,8

Assert on the postgres count output using bash

I would like to make an assertion the output of a postgres query using bash. Concretely, I am writing a bash job that counts the number of rows and if the count is not equal to zero, does something to raise alert.
$ psql MY_DATABASE -c "SELECT COUNT(*) WHERE foo=bar"
count
-------
0
(1 row)
In my script, I would like to assert that the output of above query is zero. However I am not sure where to begin because the output is not a number, but a formatted multi line string.
Is there an option in psql that makes it output a single number when counting, or could you think of any other approaches?
I would suggest to use temporary file to redirect the output and use it. Once your work is done, delete the temp file.
psql your_database -c "SELECT COUNT(*) as Count from table_a where c1=something" -t >assert.tmp
line=$(head -n 1 assert.tmp)
if [ $line -ge 0 ]; then
echo "greater then 0 and values is--"$line
fi
rm assert.tmp
Hope it works for you.

I stored values in Multi Dimensional Array. When printing the values, it's printing the second iteration values and first iteration is being replaced

This block of code is looping through a file and loading each word into a multi dimensional array.
lcv=0
declare -A db
while read line;
do
lcv1=0
echo $line
for i in $line;
do
db[$lcv,$lcv1]=$i
echo $lcv,$lcv1,${db[$lcv,$lcv1]};
#echo ${db[$lcv]}
((++lcv1))
done
((++lcv))
done < data.txt # File Contains records of 4 fields.
echo ${db[0,1]}
echo ${db[0,0]}
Little pseudo 2D array using bash
I just re-use your algorithm, whiping all echo and useless steps.
#!/bin/bash
unset x y db
y=0
declare -A db
while read line ;do
for i in $line ;do
db[$((x++)),$y]=$i
done
((y++))
x=0
done <<<$'0 1 2 3\n4 5 6 7\n8 9 a b\nc d e f'
Now if you
declare -p db x y
bash will print:
declare -A db='([0,0]="0" [0,1]="4" [0,2]="8" [0,3]="c" [3,3]="f" [3,2]="b" [3,1]="7" [3,0]="3" [2,2]="a" [2,3]="e" [2,0]="2" [2,1]="6" [1,1]="5" [1,0]="1" [1,3]="d" [1,2]="9" )'
declare -- x="0"
declare -- y="4"
At this point, I just wanna purpose to change 9th line: ((y++)) by ((y++,maxx=maxx>x?maxx:x)). This will populate maxx (to 4 in this sample)
Then inverting the array:
for i in {0..4};do # this syntax is nice, but don't support variables
for((j=0;j<y;j++)){ # this syntaxe could use variables
echo -n ${db[$i,$j]}\
}
echo
done
will print:
0 4 8 c
1 5 9 d
2 6 a e
3 7 b f
If the data.txt contains this:
$ cat data.txt
l0val0 l0val1 l0val2 l0val3
l1val0 l1val1 l1val2 l1val3
l2val0 l2val1 l2val2 l2val3
l3val0 l3val1 l3val2 l3val3
l4val0 l4val1 l4val2 l4val3
l5val0 l5val1 l5val2 l5val3
Your program produce this:
$ ./script
l0val0 l0val1 l0val2 l0val3
0,0,l0val0
0,1,l0val1
0,2,l0val2
0,3,l0val3
l1val0 l1val1 l1val2 l1val3
1,0,l1val0
1,1,l1val1
1,2,l1val2
1,3,l1val3
l2val0 l2val1 l2val2 l2val3
2,0,l2val0
2,1,l2val1
2,2,l2val2
2,3,l2val3
l3val0 l3val1 l3val2 l3val3
3,0,l3val0
3,1,l3val1
3,2,l3val2
3,3,l3val3
l4val0 l4val1 l4val2 l4val3
4,0,l4val0
4,1,l4val1
4,2,l4val2
4,3,l4val3
l5val0 l5val1 l5val2 l5val3
5,0,l5val0
5,1,l5val1
5,2,l5val2
5,3,l5val3
l0val1
l0val0
That goes to show that the value of $lcv selects each row (line), and the value of $lcv1 selects each word (record) divided on spaces or tabs.
It is working correctly from what I can see.
If we add this lines at the end of the script:
echo "end of first script"
for i in {0..5}; do
for j in {0..3}; do
printf 'db[%s,%s]=%s ' "$i" "$j" "${db[$i,$j]}"
done
echo
done
echo
declare -p db
We will get this output:
end of first script
db[0,0]=l0val0 db[0,1]=l0val1 db[0,2]=l0val2 db[0,3]=l0val3
db[1,0]=l1val0 db[1,1]=l1val1 db[1,2]=l1val2 db[1,3]=l1val3
db[2,0]=l2val0 db[2,1]=l2val1 db[2,2]=l2val2 db[2,3]=l2val3
db[3,0]=l3val0 db[3,1]=l3val1 db[3,2]=l3val2 db[3,3]=l3val3
db[4,0]=l4val0 db[4,1]=l4val1 db[4,2]=l4val2 db[4,3]=l4val3
db[5,0]=l5val0 db[5,1]=l5val1 db[5,2]=l5val2 db[5,3]=l5val3
declare -A db=([1,1]="l1val1" [1,0]="l1val0" [1,3]="l1val3" [1,2]="l1val2" [0,0]="l0val0" [0,1]="l0val1" [0,2]="l0val2" [0,3]="l0val3" [5,1]="l5val1" [5,0]="l5val0" [5,3]="l5val3" [5,2]="l5val2" [3,3]="l3val3" [3,2]="l3val2" [3,1]="l3val1" [3,0]="l3val0" [2,2]="l2val2" [2,3]="l2val3" [2,0]="l2val0" [2,1]="l2val1" [4,0]="l4val0" [4,1]="l4val1" [4,2]="l4val2" [4,3]="l4val3" )
Now, the question is: What do you think that is wrong?.

Iterating through PL/SQL result in SHELL

I want to iterate over PL/SQL rows in SHELL script and for each row I want to execute some code using current row. At this point I got:
VALUE='sqlplus -s /nolog <<EOF
CONNECT ${CONNECT}
select smth from table;
/
EXIT
EOF'
for i in "${VALUE[#]}"
do
##some code using "i" variable
done
At this point for 5 rows code executes only once. It appears it doesn't iterate at all. Any ideas how can I fix that?
You can iterate your resultset as follows:
SQL> select car_model from available_models
2 group by car_model ;
CAR_MODEL
------------------------------
Corsair
Executive
Country Squire
SQL>
Rewrite your shell script (using WHILE) as follows:
[oracle#ora12c 1]$ cat test.sh
CONNECT='z_test/welcome1'
VALUE=`sqlplus -s /nolog <<EOF
CONNECT ${CONNECT}
set head off
select car_model from available_models
group by car_model
/
EXIT
EOF`
echo "resultset: "
echo "${VALUE}"
echo " "
echo "now iterate ..."
let rec=0
echo "${VALUE}" |while read line
do
echo "processing $rec: $line"
let rec=rec+1
done
[oracle#ora12c 1]$
And this is the expected output:
[oracle#ora12c 1]$ ./test.sh
resultset:
Corsair
Executive
Country Squire
now iterate ...
processing 0:
processing 1: Corsair
processing 2: Executive
processing 3: Country Squire
Note that line #0 is blank and is expected, because is part of your resultset too.
Adding "set pagesize 0" will remove this blank line:
[oracle#ora12c 1]$ cat test.sh
CONNECT='z_test/welcome1'
VALUE=`sqlplus -s /nolog <<EOF
CONNECT ${CONNECT}
set head off
set pagesize 0
select car_model from available_models
group by car_model
/
EXIT
EOF`
......
Expected run:
[oracle#ora12c 1]$ ./test.sh
resultset:
Corsair
Executive
Country Squire
now iterate ...
processing 0: Corsair
processing 1: Executive
processing 2: Country Squire
[oracle#ora12c 1]$
Regards

Resources